package com.tbit.uqbike.client.service.impl;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.date.DateUnit;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.google.common.collect.Lists;
import com.tbit.common.constant.*;
import com.tbit.common.entity.ArrearsLog;
import com.tbit.common.entity.MachineOpLog;
import com.tbit.common.entity.UserCapitalChange;
import com.tbit.uqbike.center.pojo.MachineStatus;
import com.tbit.uqbike.client.business.machine_op_log.service.MachineOpLogService;
import com.tbit.uqbike.client.config.RefundSwitchConfig;
import com.tbit.uqbike.client.constant.*;
import com.tbit.uqbike.client.constant.enums.ControlWay;
import com.tbit.uqbike.client.dao.capital.CardUseLogDao;
import com.tbit.uqbike.client.dao.core.*;
import com.tbit.uqbike.client.dao.log.BorrowPosLogDao;
import com.tbit.uqbike.client.dao.log.OrderLogDao;
import com.tbit.uqbike.client.dao.log.UserRidingScoreLogDao;
import com.tbit.uqbike.client.dao.ter.TerInfoDao;
import com.tbit.uqbike.client.exception.BaseException;
import com.tbit.uqbike.client.pojo.*;
import com.tbit.uqbike.client.pojo.dto.UserDetailUpdateDTO;
import com.tbit.uqbike.client.pojo.newEntity.*;
import com.tbit.uqbike.client.pojo.vo.*;
import com.tbit.uqbike.client.remote.DataUtils;
import com.tbit.uqbike.client.remote.WxzffModelMsgUtil;
import com.tbit.uqbike.client.service.*;
import com.tbit.uqbike.client.service.prohibitarea.CheckProhibitAreaResult;
import com.tbit.uqbike.client.service.pushMsg.WxPushService;
import com.tbit.uqbike.client.util.*;
import com.tbit.uqbike.client.vo.borrow.BorrowBikeVO;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.task.TaskExecutor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.text.DecimalFormat;
import java.time.Duration;
import java.time.LocalDateTime;
import java.time.temporal.ChronoUnit;
import java.util.*;
import java.util.concurrent.ThreadLocalRandom;
import java.util.function.Function;
import java.util.stream.Collectors;

import static com.tbit.uqbike.client.service.impl.UserServiceImpl.*;

@Service("machineService")
@Slf4j
public class MachineServiceImpl implements MachineService {

    private static DecimalFormat df = new DecimalFormat(".#########");

    /* private final static org.apache.logging.log4j.Logger logger = LogManager.getLogger(LogManager.ROOT_LOGGER_NAME);*/
    @Autowired
    private MachineDao machineDao;
    @Autowired
    private MachineBorrowService machineBorrowService;
    @Autowired
    private UserService userService;
    @Autowired
    private AccountService accountService;
    @Autowired
    private RideLogService rideLogService;
    @Autowired
    private UserAccountLogService userAccountLogService;
    @Autowired
    private ParkPointService parkPointService;
    @Autowired
    private TerControlService terControlService;
    @Autowired
    private HistoryService historyService;
    @Autowired
    private ParkService parkService;
    @Autowired
    private ParkMachineService parkMachineService;
    @Autowired
    private UserBlackListService userBlackListService;
    @Autowired
    private RideCardService rideCardService;
    @Autowired
    private RideCardUserService rideCardUserService;
    @Autowired
    private RedisService redisService;
    @Autowired
    private MachineStatusService machineStatusService;
    @Autowired
    private DispatchLogService dispatchLogService;
    @Autowired
    private GiftCardUserService giftCardUserService;
    @Autowired
    private CouponUserService couponUserService;
    @Autowired
    private AdAccountDepositService adAccountDepositService;
    @Autowired
    private AdAccountFeeService adAccountFeeService;
    @Autowired
    private UserToAgentService userToAgentService;
    @Autowired
    private AgentDepositVipService agentDepositVipService;
    @Autowired
    private OrderLogDao orderLogDao;
    @Autowired
    private RideLogDetailService rideLogDetailService;
    @Autowired
    private AccountConfigService accountConfigService;
    @Autowired
    private ProhibitAreaService prohibitAreaService;
    @Autowired
    private MachineFaultService machineFaultService;
    @Autowired
    private BleBeaconDao bleBeaconDao;
    @Autowired
    private MachineFunctionDao machineFunctionDao;
    @Autowired
    private GeoService geoService;
    @Autowired
    private VipCardLogService vipCardLogService;
    @Autowired
    private MachineLastUseService machineLastUseService;
    @Autowired
    private AppointMentDao appointMentDao;
    @Autowired
    private ParkPointVertDao parkPointVertDao;
    @Autowired
    private PayPointService payPointService;
    @Autowired
    private UserWxzffDao userWxzffDao;
    @Autowired
    private WeixinConfigDao weixinConfigDao;
    @Autowired
    private WeixinPayConfigService weixinPayConfigService;
    @Autowired
    private TerInfoDao terInfoDao;
    @Autowired
    private MachineOccupyDao machineOccupyDao;
    @Autowired
    private InvitationJoinService invitationJoinService;
    @Autowired
    private InviteNewEventService inviteNewEventService;
    @Autowired
    private ParkFeatureService parkFeatureService;
    @Autowired
    private VipFeeDao vipFeeDao;
    @Autowired
    private DispatchConfigService dispatchConfigService;
    @Autowired
    private LeaderAccountDao leaderAccountDao;
    @Autowired
    private CacheService cacheService;
    @Autowired
    private EventDetailService eventDetailService;
    @Autowired
    private GeoReMachineDao geoReMachineDao;
    @Autowired
    private XycxBorrowCheckService xycxBorrowCheckService;
    @Autowired
    private ActiveStatService activeStatService;
    @Autowired
    private WeixinPayService weixinPayService;
    @Autowired
    private UserDiscountStatService userDiscountStatService;
    @Autowired
    private SpecialAuthService specialAuthService;
    @Autowired
    private PushMsgLogService pushMsgLogService;
    @Autowired
    private UserAccountMoneyService userAccountMoneyService;
    @Autowired
    private RidePayLogService ridePayLogService;
    @Autowired
    private CardUseLogDao cardUseLogDao;
    @Autowired
    private RideUseLogService rideUseLogService;
    @Autowired
    private RechargeMerchantService rechargeMerchantService;
    @Autowired
    private UserRidingScoreLogDao userRidingScoreLogDao;
    @Autowired
    private MQProducer mqProducer;
    @Autowired
    private ReturnBikeMsgLogService returnBikeMsgLogService;
    @Autowired
    private VerticalParkLogService verticalParkLogService;
    @Autowired
    private MachineStatusConsumerService machineStatusConsumerService;
    @Autowired
    private BorrowPosLogDao borrowPosLogDao;
    @Resource
    private RefundSwitchConfig refundSwitchConfig;
    @Autowired
    private TaskExecutor taskExecutor;
    @Resource
    private WxPushService wxPushService;
    @Autowired
    private AccountConfigExtDao accountConfigExtDao;
    @Autowired
    private MachineOpLogService machineOpLogService;
    @Autowired
    private MachineServiceImpl machineService;

    @Autowired
    private BleHelmetService bleHelmetService;
    @Resource
    private RechargeService rechargeService;
    @Autowired
    private TerAttrExService terAttrExService;
    @Autowired
    private OrderHelmetLostLogService orderHelmetLostLogService;
    @Resource
    private MemberLogService memberLogService;

    @Autowired
    private UserDetailService userDetailService;
    @Autowired
    private UserWeightLogService userWeightLogService;
    @Resource
    private MachineFunctionService machineFunctionService;
    @Resource
    private RideLogDetailsService rideLogDetailsService;


    private static final ThreadLocal<MachineFault> machineFaultThreadLocal = new ThreadLocal<>();

    /**
     * 向上取整
     *
     * @param d
     * @return
     */
    public static double formatDouble(double d) {
        BigDecimal bg = new BigDecimal(d).setScale(0, RoundingMode.UP);
        return bg.doubleValue();
    }

    @Override
    public Machine getByMachineNO(String machineNO) {
        return machineDao.getByMachineNO(machineNO);
    }

    @Override
    public List<Machine> getByMachineNOs(List<String> machineNOs) {
        Map<String, Object> params = new HashMap<String, Object>();
        params.put("machineNOs", StringUtil.getArrayListString(machineNOs));

        return machineDao.getByMachineNOs(params);
    }

    @Override
    public List<Machine> getByMachineIds(List<Integer> machineIds) {
        Map<String, Object> params = new HashMap<String, Object>();
        params.put("machineIds", StringUtil.getListString(machineIds));

        return machineDao.getByMachineIds(params);
    }

    @Override
    public Machine getByMachineId(Integer machineId) {
        return machineDao.getByMachineId(machineId);
    }

    @Override
    public RideLog getBorrowing(Integer userId) {
        return rideLogService.getByUserIdNotFinish(userId);
    }

    @Override
    public List<Machine> getCanBorrow(Integer accountId, List<String> machineNOs) {
        Map<String, Object> params = new HashMap<String, Object>();
        params.put("accountId", accountId);
        params.put("machineNOs", StringUtil.getArrayListString(machineNOs));

        return machineDao.getCanBorrow(params);
    }

    @Override
    public void updateLastStatus(Integer machineId, Integer lastUserId) {
        Map<String, Object> params = new HashMap<String, Object>();
        params.put("machineId", machineId);
        params.put("lastUserId", lastUserId);
        params.put("lastBorrowTime", DateTimeUtil.getNowTime());

        machineDao.updateLastStatus(params);
    }

    @Override
    public Integer findBrandId(String machineNO) {
        return machineDao.findBrandId(machineNO);
    }

    @Override
    public Machine getByUserCode(String userCode) {
        return machineDao.getByUserCode(userCode);
    }

    @Override
    public void checkIsOk(User user, String userCode, Point point) {

        /**用户借车车辆之前提示用户金额是否为负的*/
        User userdb = userService.getByUserId(user.getUserId());

        Machine machine = getByUserCode(userCode);
        if (machine == null) {
            throw new BaseException("-30005", "该编号不存在");
        }
        /**更新最后扫码人信息*/
        machineLastUseService.updateLastUse(machine.getMachineId(), machine.getMachineNO(), point, userdb.getUserId(), userdb.getPhone());

        if (machine.getState().equals(MachineStateConstant.MACHINE_STATE_DTF)) {
            throw new BaseException("-3029", "故障车辆，车辆还没有投放");
        }
        if (machine.getState().equals(MachineStateConstant.MACHINE_STATE_DDD)) {
            throw new BaseException("-3029", "故障车辆，车辆还没有投放");
        }

        if (machine.getState().equals(MachineStateConstant.MACHINE_STATE_GZ)) {
            throw new BaseException("-3028", "故障车辆，请更换一辆");
        }

        if (machine.getState().equals(MachineStateConstant.MACHINE_STATE_BF)) {
            throw new BaseException("-3028", "报废车辆，请更换一辆");
        }

        MachineBorrow machineBorrow = machineBorrowService.getByMachineId(machine.getMachineId());
        if (machineBorrow != null) {
            if (!user.getUserId().equals(machineBorrow.getUserId())) {
                /**用户再次扫码,跟之前借车用户不一致，结束订单*/
                User userOld = userService.getByUserId(machineBorrow.getUserId());
                /**自动结束订单*/
                remand(userOld, 200, point, 1, true);
            }
            log.info("单车用户再次扫码，自动结束前一个订单" + machineBorrow.getOrderNO());

        }
        /**运营区域判断*/
        List<Geo> geos = geoService.getByAccountId(machine.getAccountId());
        Boolean result = isInArea(geos, machine.getAccountId(), point, machine.getMachineId());
        if (!result) {
            throw new BaseException("-3047", "运营区域外禁止借车");
        }


    }

    @Override
    public void borrow(User user, String userCode, String borrowPosLogId) {
        Machine machine = getByUserCode(userCode);
        if (machine == null) {
            throw new BaseException("-30005", "该编号不存在");
        }
        /**设备是否被预约*/
        checkAppoint(machine, user.getUserId(), null);

        String orderNO = StringUtil.getRideLogOrderNO();
        /**判断是否已经借出*/
        MachineBorrow machineBorrowDB = machineBorrowService.getByMachineId(machine.getMachineId());
        if (machineBorrowDB == null) {
            /**添加到最后借出表中*/
            MachineBorrow machineBorrowAdd = new MachineBorrow();
            machineBorrowAdd.setOrderNO(orderNO);
            machineBorrowAdd.setUserId(user.getUserId());
            machineBorrowAdd.setMachineId(machine.getMachineId());
            machineBorrowAdd.setCreateTime(new Date());
            machineBorrowService.add(machineBorrowAdd);

            /**添加到骑行记录表中*/
            RideLog rideLog = new RideLog();
            rideLog.setOrderNO(orderNO);
            rideLog.setAccountId(machine.getAccountId());
            rideLog.setMachineId(machine.getMachineId());
            rideLog.setStartTime(DateTimeUtil.getNowTime());
            rideLog.setUserId(user.getUserId());
            rideLog.setMachineNO(machine.getMachineNO());
            rideLog.setUserCode(userCode);
            /**兼容站点借出数据统计功能，记录车辆解除站点*/
            ParkMachine parkMachine = parkMachineService.getByMachineId(machine.getMachineId());
            if (parkMachine != null) {
                rideLog.setStartPointId(parkMachine.getParkPointId());
            }
            rideLogService.insert(rideLog);

            /**区域用户绑定*/
            userToAgentService.insert(user.getUserId(), machine.getAccountId());
            /**更新车辆最后状态*/
            updateLastStatus(machine.getMachineId(), user.getUserId());
            log.info("用户" + user.getUserId() + "借车时间： " + DateTimeUtil.getNowTime());

            //将orderNo存入borrowPosLog
            if (StringUtils.isNotBlank(borrowPosLogId)) {
                Long id = Long.parseLong(borrowPosLogId);
                borrowPosLogDao.updateOrderNoById(id, orderNO);
            }

        } else {
            log.warn(machine.getMachineNO() + ",借车失败，车辆已经被借出");
        }
    }

    @Override
    public Dispatch areaCheck(Integer userId, String userCode, Point point, Dispatch dispatchDB) {
        Dispatch dispatch = new Dispatch(DisPatchConstant.DISPATCH_PARK, 0.0);
        Machine machine = getByUserCode(userCode);
        if (machine == null) {
            throw new BaseException("-30005", "该编号不存在");
        }
        MachineBorrow machineBorrow = machineBorrowService.getByUserId(userId);
        if (machineBorrow == null) {
            throw new BaseException("-3006", "不存在借出记录");
        }
        RideLog rideLog = rideLogService.getByOrderNO(machineBorrow.getOrderNO());
        if (rideLog == null) {
            throw new BaseException("-3007", "不存在骑行记录");
        }

        /**判断是否在站点*/
        ParkPointInfo parkPointInfo = isInParkPoint(machine.getAccountId(), machine.getMachineNO(), point,
                MapConstant.MAP_GOOGLE);
        Integer parkPointId = parkPointInfo.getParkPointId();
        CheckProhibitAreaResult checkInProhibitArea = prohibitAreaService.checkInProhibitArea(machine.getAccountId(), point);
        Integer prohibitAreaId = checkInProhibitArea.inProhibitArea() ? checkInProhibitArea.getProhibitArea().getProhibitAreaId() : null;
        AdAccountFee adAccountFee = adAccountFeeService.getByAccountId(machine.getAccountId(), machine.getMachineId());
        boolean noParkAreaSwitchOpen = Optional.ofNullable(adAccountFee.getNoParkAreaSwitch())
                .map(s -> s.equals(1))
                .orElse(false);

        /**判断下是否在区域内*/
        List<Geo> geos = geoService.getByAccountId(machine.getAccountId());
        Boolean result = isInArea(geos, machine.getAccountId(), point, machine.getMachineId());
        if (parkPointId == null) {

            if (adAccountFee.getDispatchSwitch() == 1) {
                if (!result) {
                    if (adAccountFee.getAreaMoney() > 0) {
                        dispatch = new Dispatch(DisPatchConstant.DISPATCH_NOT_IN_AREA, (double) adAccountFee.getAreaMoney());
                    }
                } else {
                    if (adAccountFee.getParkPointMoney() > 0) {
                        dispatch = new Dispatch(DisPatchConstant.DISPATCH_NO_IN_PARK, (double) adAccountFee.getParkPointMoney());
                    }
                }
            }
            //没有开启站外/超区 调度费开关，同时没有开启禁停区调度开关/不在禁停区，不可还车
            else if (!noParkAreaSwitchOpen || prohibitAreaId == null) {
                throw new BaseException("-3008", "请在停车点内还车");
            }
        }

        //如果开启了禁停区调度开关，且在禁停区
        if (noParkAreaSwitchOpen && prohibitAreaId != null && result) {
            double noParkAreaMoney = (double) Optional.ofNullable(adAccountFee.getNoParkAreaMoney()).orElse(0);
            dispatch = new Dispatch(DisPatchConstant.DISPATCH_NO_PARK_AREA, noParkAreaMoney);
        }

        /**计算骑行费用*/
        Integer time = DateTimeUtil.getMinDiffer(rideLog.getStartTime());
        /**计算费用*/
        double rideMoney = MoneyUtil.getRideFee(time, 0.0, adAccountFee, adAccountFee.getFreeTime());
        /**存入内存*/
        redisService.add(RedisConstant.USER_DC_SIGN + userId, String.valueOf(time), 60);
        dispatchDB.setMoney(rideMoney);
        return dispatch;
    }

    /**
     * 获取调度金额
     *
     * @param dispatchType
     * @param adAccountFee
     * @return
     */

    public Double getDispatchMoney(Integer dispatchType, AdAccountFee adAccountFee, Integer secondType, DisPatchData disPatchData) {
        Double dispatchMoney = null;
        if (supportDispatch(dispatchType)) {
            // 开启调度费开关
            if (adAccountFee != null) {
                if (adAccountFee.getNoParkAreaSwitch() == 1) {
                    if (dispatchType.equals(DisPatchConstant.DISPATCH_NO_PARK_AREA)) {
                        dispatchMoney = (double) adAccountFee.getNoParkAreaMoney();
                        disPatchData.setNoParkAreaMoney(dispatchMoney);
                    }
                }
                if (adAccountFee.getDispatchSwitch() == 1) {
                    if (dispatchType.equals(DisPatchConstant.DISPATCH_NOT_IN_AREA)) {
                        dispatchMoney = (double) adAccountFee.getAreaMoney();
                        disPatchData.setAreaMoney(dispatchMoney);
                    } else if (dispatchType.equals(DisPatchConstant.DISPATCH_NO_IN_PARK) || dispatchType
                            .equals(DisPatchConstant.DISPATCH_OVERFLOW_PARK)) {
                        dispatchMoney = (double) adAccountFee.getParkPointMoney();
                        disPatchData.setParkMoney(dispatchMoney);
                    }
                }
            }
        }
        if (secondType != null && secondType.equals(DisPatchConstant.DISPATCH_HELMET)) {
            //开启头盔调度开关
            if (adAccountFee != null && adAccountFee.getHelmetSwitch() == 1) {
                Double helmetMoney = (double) adAccountFee.getHelmetMoney();
                disPatchData.setHelmetMoney(helmetMoney);
                dispatchMoney = dispatchMoney == null ? helmetMoney : NumberUtil.add(dispatchMoney, helmetMoney);
            }
        }
        return dispatchMoney;
    }

    /**
     * 判断是否支持调度
     *
     * @param dispatchType
     * @return
     */
    private boolean supportDispatch(Integer dispatchType) {
        return Objects.nonNull(dispatchType) && (dispatchType.equals(DisPatchConstant.DISPATCH_NOT_IN_AREA)
                || dispatchType.equals(DisPatchConstant.DISPATCH_NO_IN_PARK)
                || dispatchType.equals(DisPatchConstant.DISPATCH_OVERFLOW_PARK)
                || dispatchType.equals(DisPatchConstant.DISPATCH_NO_PARK_AREA));
    }


    @Override
    public void remand(User user, Integer money, Point point, Integer dispatchType, Boolean auto) {
        double dispatchMoney = 0;
        MachineBorrow machineBorrow = machineBorrowService.getByUserId(user.getUserId());
        if (machineBorrow != null) {
            RideLog rideLog = rideLogService.getByOrderNO(machineBorrow.getOrderNO());
            if (rideLog == null) {
                throw new BaseException("-3007", "不存在骑行记录");
            }
            Machine machine = machineDao.getByMachineId(machineBorrow.getMachineId());
            // todo 节假日计费
            AdAccountFee adAccountFee = adAccountFeeService.getByAccountId(machine.getAccountId(), machine.getMachineId());

            dispatchMoney = getDispatchMoney(dispatchType, adAccountFee, null, new DisPatchData());
            ParkPointInfo parkPointInfo = isInParkPoint(machine.getAccountId(), machine.getMachineNO(), point,
                    MapConstant.MAP_GOOGLE);
            Integer parkPointId = parkPointInfo.getParkPointId();
            /**骑行时长*/
            Integer time = DateTimeUtil.getMinDiffer(rideLog.getStartTime());
            if (redisService.get(RedisConstant.USER_DC_SIGN + user.getUserId()) != null) {
                time = Integer.valueOf(redisService.get(RedisConstant.USER_DC_SIGN + user.getUserId()));
            }
            /**计算费用*/
            double rideMoney = MoneyUtil.getRideFee(time, 0.0, adAccountFee, adAccountFee.getFreeTime());
            /**查询用户实时数据*/
            user = userService.getByUserId(user.getUserId());
            /**删除当前骑行记录*/
            machineBorrowService.del(rideLog.getMachineId());
            Boolean isPay = false;
            if (user.getMoney() >= (rideMoney + dispatchMoney)) {
                isPay = true;
            }
            /**扣取费用*/
            userService.updateMoney(user.getUserId(), -(double) (rideMoney + dispatchMoney));
            UserAccountLog userAccountLog = new UserAccountLog();
            userAccountLog.setMoneyType(0);
            /**记录扣费日志*/
            userAccountLog.setAccountId(machine.getAccountId());
            userAccountLog.setMoney(rideMoney);
            userAccountLog.setUserId(user.getUserId());
            userAccountLog.setOperaTime(DateTimeUtil.getNowTime());
            userAccountLog.setRemark("单车骑行扣费");
            userAccountLog.setType(0);
            userAccountLogService.insert(userAccountLog);

            if (dispatchMoney > 0) {
                UserAccountLog userAccountLogDB = new UserAccountLog();
                userAccountLogDB.setMoneyType(0);
                /**记录扣费日志*/
                userAccountLogDB.setAccountId(machine.getAccountId());
                userAccountLogDB.setMoney(dispatchMoney);
                userAccountLogDB.setUserId(user.getUserId());
                userAccountLogDB.setOperaTime(DateTimeUtil.getNowTime());
                userAccountLogDB.setRemark("单车骑行调度扣费");
                userAccountLogDB.setType(0);
                userAccountLogService.insert(userAccountLogDB);
            }

            //更新骑行日志
            rideLog.setEndTime(DateTimeUtil.getNowTime());
            if (isPay) {
                rideLog.setPayTime(DateTimeUtil.getNowTime());//设置支付时间
            }
            rideLog.setMileage(0.0);
            rideLog.setMoney(rideMoney + dispatchMoney);
            rideLog.setStopTime(rideLog.getStopTime());
            Integer pointId = parkPointId;
            //兼容 超区时parkPointId=-2的旧逻辑，后面应当考虑在rideLog中增加禁停区信息
            if (parkPointId == null) {
                if (!parkPointInfo.isInArea()) {
                    pointId = -2;
                } else {
                    log.error("还车位置异常");
                }
            }
            rideLog.setEndPointId(pointId);
            if (dispatchMoney > 0) {
                rideLog.setRemark("单车骑行费用=" + rideMoney * 1.0 / 100 + "元,调度费用=" + dispatchMoney * 1.0 / 100);
            } else {
                rideLog.setRemark("单车骑行费用" + rideMoney * 1.0 / 100 + "元");
            }

            rideLogService.updateByOrderNOSelective(rideLog);


            /**调度车辆记录调度日志*/
            if (dispatchMoney > 0) {
                DispatchLog dc = new DispatchLog();
                dc.setAccountId(rideLog.getAccountId());//代理商id,非品牌id
                dc.setLat(point.getY());
                dc.setLon(point.getX());
                dc.setUserCode(machine.getUserCode());
                dc.setMachineNO(machine.getMachineNO());
                dc.setOrderNO(machineBorrow.getOrderNO());
                dc.setReturnTime(DateTimeUtil.getNowTime());
                dc.setMoney(dispatchMoney);
                dc.setPay(isPay);
                dispatchLogService.insert(dc);
            }

        }
    }

    @Override
    public Boolean checkNewParkVoice(String userCode) {
        Machine machine = machineDao.getByUserCode(userCode);
        if (machine == null) {
            throw new BaseException("-30005", "设备不存在");
        }
        TerInfo terInfo = terInfoDao.getByMachineNO(machine.getMachineNO());
        if (terInfo != null && (
                DateTimeUtil.getTimeDF("2020-08-01 00:00:00", terInfo.getJoinTime()) > 0 || SystemData.machineSofewareInfoMap.containsKey(terInfo.getSoftVersion()))) {
            return true;
        } else {
            return false;
        }
    }

    @Override
    public List<Machine> getFreeByMachineNOs(List<String> machineNOs) {
        Map<String, Object> params = new HashMap<String, Object>();
        params.put("machineNOs", StringUtil.getArrayListString(machineNOs));

        return machineDao.getFreeByMachineNOs(params);
    }

    @Override
    public List<Machine> getFreeByMachineNOs(List<String> machineNOs, Set<Integer> accountIds) {
        Map<String, Object> params = new HashMap<String, Object>();
        params.put("machineNOs", machineNOs);
        params.put("accountIds", accountIds);

        return machineDao.getFreeByMachineNOsAndAccountId(params);
    }

    @Override
    public ParkPointInfo isInParkPoint(Integer accountId, String machineNO, Point point, Integer mapType) {
        Integer parkPointId = null;
        Integer prohibitAreaId = null;
        AccountConfig accountConfig = accountConfigService.getByAccountId(accountId);
        Machine machine = machineDao.getByMachineNO(machineNO);
        if (machine == null) {
            throw new BaseException("-30005", "设备不存在");
        }
        AdAccountFee adAccountFee = adAccountFeeService.getByAccountId(accountId, machine.getMachineId());
        boolean noParkAreaSwitchOpen = Optional.ofNullable(adAccountFee.getNoParkAreaSwitch())
                .map(s -> s.equals(1))
                .orElse(false);
        boolean isProhibitAreaReturnMode = Optional.ofNullable(accountConfig)
                .map(AccountConfig::getReturnModel)
                .map(s -> s.equals(1))
                .orElse(false);
        MachineBorrow machineBorrow = machineBorrowService.getByMachineId(machine.getMachineId());
        if (machineBorrow == null) {
            throw new BaseException("-3053", "不存在订单数据");
        }
        MachineStatus machineStatus = machineStatusService.getByMachineNO(machineNO, mapType);

        ParkPointInfo parkPointInfo = new ParkPointInfo();
        parkPointInfo.setEnableNoParkArea(noParkAreaSwitchOpen);
        parkPointInfo.setMachineId(machine.getMachineId());
        parkPointInfo.setMobilePoint(new Point(point.getX(), point.getY()));
        parkPointInfo.setMachinePoint(new Point(machineStatus.getLon(), machineStatus.getLat()));
        parkPointInfo.setProhibitAreaReturnMode(isProhibitAreaReturnMode);
        /***/
        Boolean b = false;
        /**用客户端手机位置判断*/
        if (point != null) {
            /**默认客户手机位置有效*/
            double distance = 0D;
            /**如果车辆位置存在，并且车辆电量时间距离当前时间小于5分钟，则需要考虑还车手机位置无效情况*/
            if (machineStatus != null && machineStatus.getBatDt() != null && DateTimeUtil.getMinDiffer(machineStatus.getBatDt()) < 5) {
                distance = GeoUtil
                        .getDistanceBy(machineStatus.getLon(), machineStatus.getLat(), point.getX(), point.getY());
            }
            /**如果客户手机位置和车辆位置距离大于100米，认为手机位置无效*/
            if (distance < 100) {
                if (noParkAreaSwitchOpen) {
                    CheckProhibitAreaResult checkInProhibitArea = prohibitAreaService.checkInProhibitArea(accountId, point);
                    prohibitAreaId = checkInProhibitArea.inProhibitArea() ? checkInProhibitArea.getProhibitArea().getProhibitAreaId() : null;
                }
                /**兼容禁停区模式*/
                if (isProhibitAreaReturnMode) {
                    /**禁停区模式需要判断是否在区域内*/
                    List<Geo> geos = geoService.getByAccountId(machine.getAccountId());
                    b = isInArea(geos, machine.getAccountId(), point, machine.getMachineId());
                } else {

                    parkPointId = parkPointService.checkInParkPoint(accountId, point, machine.getMachineId(), null);
                }
                log.info("[" + machineNO + "]还车位置[客户端]：lo :" + df.format(point.getX()) + ",la :" + df.format(point.getY()));

            } else {
                log.warn("手机位置距离车辆过远" + machineBorrow.getOrderNO());
            }
            /**还车记录还车位置*/
			/*ReturnPosLog returnPosLog = new ReturnPosLog();
			returnPosLog.setLon(point.getX());
			returnPosLog.setLat(point.getY());
			returnPosLog.setMachineNO(machineNO);
			returnPosLog.setUserCode(machine==null?"":machine.getUserCode());
			returnPosLog.setOrderNO(machineBorrow.getOrderNO());
			returnPosLog.setPosFrom(ReturnPosLogConstant.TYPE_PGONE);
			returnPosLog.setPosTime(new Date());
			returnPosLogDao.add(returnPosLog);*/
        }

        /**用车辆位置再次判断*/
        if (parkPointId == null && prohibitAreaId == null) {
            /** 获取车辆位置 */
            if (machineStatus == null) {
                return null;
            }
            if (machineStatus.getLon() == null || machineStatus.getLat() == null) {
                return null;
            }
            if (noParkAreaSwitchOpen) {
                CheckProhibitAreaResult checkInProhibitArea = prohibitAreaService.checkInProhibitArea(accountId, point);
                prohibitAreaId = checkInProhibitArea.inProhibitArea() ? checkInProhibitArea.getProhibitArea().getProhibitAreaId() : null;
            }
            point = new Point(machineStatus.getLon(), machineStatus.getLat());
            /**兼容禁停区模式*/
            if (isProhibitAreaReturnMode) {
                /**禁停区模式需要判断是否在区域内*/
                if (!b) {
                    List<Geo> geos = geoService.getByAccountId(machine.getAccountId());
                    b = isInArea(geos, machine.getAccountId(), point, machine.getMachineId());
                }
            } else {
                parkPointId = parkPointService.checkInParkPoint(accountId, point, machine.getMachineId(), null);
            }
            /**还车记录还车位置*/
			/*ReturnPosLog returnPosLog = new ReturnPosLog();
			returnPosLog.setMachineNO(machineNO);
			returnPosLog.setLon(point.getX());
			returnPosLog.setLat(point.getY());
			returnPosLog.setUserCode(machine==null?"":machine.getUserCode());
			returnPosLog.setOrderNO(machineBorrow.getOrderNO());
			returnPosLog.setPosFrom(ReturnPosLogConstant.TYPE_MACHINE);
			returnPosLog.setPosTime(new Date());
			returnPosLogDao.add(returnPosLog);*/
            log.info("[" + machineNO + "]还车位置[车辆]：lo :" + df.format(point.getX()) + ",la :" + df.format(point.getY()));
        }

        if (accountConfig != null && accountConfig.getReturnModel() == 1 && !b) {
            throw new BaseException("-3050", "运营区域外,请在运营区域内使用");
        }
        if (parkPointId == null && prohibitAreaId == null) {
            log.warn("[" + machineNO + "]终端在线网络还车失败,不在停车点或禁停区");
            //throw new BaseException("-3008", "还车失败，请在停车点内还车");
        }

        parkPointInfo.setParkPointId(parkPointId);
        parkPointInfo.setProhibitAreaId(prohibitAreaId);
        parkPointInfo.setInArea(b);

        return parkPointInfo;
    }

    @Override
    public RideLog getBorrowingInfo(Integer accountId, Integer userId, Integer online) {
        RideLog rideLog = getBorrowing(userId);

        /**查询设备编号*/
        if (rideLog != null) {
            Machine machine = getByMachineId(rideLog.getMachineId());
            if (machine != null) {
                /**暂时兼容返回用户码*/
                //rideLog.setMachineNO(machine.getMachineNO());
                rideLog.setMachineNO(machine.getUserCode());
                /**计算当前里程*/
                String value = redisService.get(RedisConstant.MILEAGE_KEY_PREFIX + userId);
                Integer mileage;
                if (value == null || online == 1) {
                    mileage = historyService.getMileageByMachineNOAndTime(machine.getMachineNO(), rideLog.getStartTime(), DateTimeUtil
                            .getNowTime());
                    if (mileage == null) {
                        mileage = 0;
                    }
                    /**存放至redis5分钟*/
                    redisService.add(RedisConstant.MILEAGE_KEY_PREFIX + userId, String.valueOf(mileage), 300);
                } else {
                    mileage = Integer.valueOf(value);
                }
				/*Integer mileage=historyService.getMileageByMachineNOAndTime(machine.getMachineNO(), rideLog.getStartTime(), DateTimeUtil.getNowTime());
				//Integer mileage=0;
				if(mileage==null){
					mileage=0;
				}*/

                double mileageDB = (double) mileage / 1000;//米转公里
                mileageDB = Double.valueOf(String.format("%.2f", mileageDB));
                rideLog.setMileage(mileageDB);

                /**计算预估费用*/
                /**总费用*/
                Double money = 0D;
                /**代理商定义费用收取*/
                //AdAccountFee adAccountFee = adAccountFeeService.getByAccountId(machine.getAccountId(), machine.getMachineId());
                AdAccountFee adAccountFee = adAccountFeeService.getFee(machine.getAccountId(), machine.getMachineId(), rideLog.getStartTime());
                Integer feeId = 0;
                if (adAccountFee != null && adAccountFee.getName() != null) {
                    feeId = adAccountFee.getFeeId();
                }
                AdAccountFee vipFee = null;
                /**查询用户购买的会员卡*/
                VipLogDot vipLogDot = vipCardLogService.getVaild(machine.getAccountId(), userId, feeId);
                if (vipLogDot != null && vipLogDot.getVipType().equals(MemberCardConstant.VIP_CARD_VIPFEE)) {
                    // vipFee = vipFeeDao.getByVipId(vipLogDot.getVipId());
                    vipFee = adAccountFeeService.getVipFee(rideLog.getAccountId(), rideLog.getStartTime(), vipLogDot.getVipId());
                }

                /**临时停车费用*/
                //Park park=parkService.getByUserId(userId);
                //money=money+parkService.getParkFee(park, rideLog,adAccountFee);

                /**免费时长*/
                Integer freeTime = 0;
                /**免费距离*/
                Integer freeDistance = 0;
                // （免费骑行） 判断 免费次数是否小于用户当日骑行次数
                if (adAccountFee != null) {
                    int freeTimeCount = adAccountFee.getFreeRideTime() == null ? 0 : adAccountFee.getFreeRideTime();

                    Integer nowDayRideCount = rideLogService.getTodayNo(userId, DateTimeUtil.getNowTimeYMD());
//                    log.info("用户{}今日已免费骑行的次数为{}",userId,nowDayRideCount);
                    if (freeTimeCount > nowDayRideCount) {
                        freeTime = adAccountFee.getFreeTime();
                        freeDistance = adAccountFee.getFreeDistance();
                    }
                }

                log.info("用户的免费骑行时长为{}", freeTime);

                rideLog.setEndTime(DateTimeUtil.getNowTime());
                /**兼容vip卡自定义计费规则*/
                if (vipFee != null) {
                    freeTime = vipFee.getFreeTime();
                    freeDistance = vipFee.getFreeDistance();
                    money = money + MoneyUtil
                            .getRide(rideLog, vipFee, mileageDB, null, freeTime, freeDistance, new ArrayList<>(), new CouponUserDto(), false);
                } else {
                    money = money + MoneyUtil
                            .getRide(rideLog, adAccountFee, mileageDB, null, freeTime, freeDistance, new ArrayList<>(), new CouponUserDto(), false);
                }
                /**特殊认证打折*/
                User user = userService.getByUserId(userId);
                AccountDiscount accountDiscount = specialAuthService.getDiscount(machine.getAccountId(), user);
                if (accountDiscount != null) {
                    Double spMoney = formatDouble((money * accountDiscount.getDiscount().doubleValue()));
                    money = spMoney;
                }
                /**兼容领导账号逻辑,不扣任何费用*/
                LeaderAccount leaderAccount = leaderAccountDao.getByPhone(user.getPhone(), user.getAccountId());
                if (leaderAccount != null && leaderAccount.getAccountId().equals(user.getAccountId()) && System.currentTimeMillis() < leaderAccount.getExpireTime().getTime()) {
                    money = 0D;
                }
                /**骑行费用*/
                //money=money+MoneyUtil.getRide(rideLog,adAccountFee,mileageDB,null,freeTime,new ArrayList<>(),new CouponUserDto());
                rideLog.setMoney(money);
            }
            MachineStatus machineStatus = machineStatusService.getByMachineNO(machine.getMachineNO(), MapConstant.MAP_GOOGLE);
            List<Geo> geos = geoService.getByAccountId(machine.getAccountId());
            if (geos.size() > 0) {
                List<Point> points = GeoUtil.getPoints(geos.get(0).getPoints());
                Boolean isInArea = GeoUtil
                        .IsPtInPoly(new Point(machineStatus.getLon(), machineStatus.getLat()), points);
                Double distance = GeoUtil
                        .getDistance(new Point(machineStatus.getLon(), machineStatus.getLat()), points);
                rideLog.setInGeo(isInArea);
                rideLog.setDistance(distance);
            }
            log.info("当前的订单信息为{}", JSON.toJSONString(rideLog));
        }
        return rideLog;
    }

    @Override
    public boolean checkIsPhoto(Integer userId, RideLog rideLog, Machine machine) {
        AdAccountFee adAccountFee = adAccountFeeService.getFee(machine.getAccountId(), machine.getMachineId(), rideLog.getStartTime());
        Integer rideTime = DateTimeUtil.getMinDiffer(rideLog.getStartTime());

        /**判断 免费次数是否小于用户当日骑行次数*/
        if (adAccountFee != null) {
            int freeTimeCount = adAccountFee.getFreeRideTime() == null ? 0 : adAccountFee.getFreeRideTime();
            Integer nowDayRideCount = rideLogService.getTodayNo(userId, DateTimeUtil.getNowTimeYMD());
            if (freeTimeCount <= nowDayRideCount) {
                return true;
            }
        }

        /**查询用户购买的会员卡*/
        Integer feeId = 0;
        if (adAccountFee != null && adAccountFee.getName() != null) {
            feeId = adAccountFee.getFeeId();
        }
        AdAccountFee vipFee = null;
        VipLogDot vipLogDot = vipCardLogService.getVaild(machine.getAccountId(), userId, feeId);
        if (vipLogDot != null && vipLogDot.getVipType().equals(MemberCardConstant.VIP_CARD_VIPFEE)) {
            vipFee = adAccountFeeService.getVipFee(rideLog.getAccountId(), rideLog.getStartTime(), vipLogDot.getVipId());
        }
        /**判断是会员骑行还是普通骑行*/
        Integer freeTime = 0;
        Integer freeDistance = 0;
        if (vipFee != null) {
            freeTime = vipFee.getFreeTime();
            freeDistance = vipFee.getFreeDistance();
        } else {
            freeTime = adAccountFee.getFreeTime();
            freeDistance = Optional.ofNullable(adAccountFee.getFreeDistance()).orElse(0);
        }
        Integer freeConditionType = Optional.ofNullable(adAccountFee.getFreeConditionType()).orElse(0);
        Integer distance = 0;
        if (freeDistance != 0) {
            distance = Optional.ofNullable(historyService.getMileageByMachineNOAndTime(machine.getMachineNO(), rideLog.getStartTime(), DateTimeUtil
                    .getNowTime())).orElse(0);
        }
        int freeType = MoneyUtil.countFreeType(rideTime, distance, freeTime, freeDistance, freeConditionType);
        return freeType < 0;
    }


    /**
     * 校验是否停运
     *
     * @param brandId   品牌id
     * @param accountId 区域id
     */
    @Override
    public Boolean checkServiceSwitch(Integer brandId, Integer accountId) {
        Boolean flag = true;
        try {
            Date date = new Date();
            JSONObject brandJsonConfig = cacheService.getBrandJsonConfig(brandId, BrandConstant.SERVICE_SWITCH);
            if (Objects.nonNull(brandJsonConfig.getString("serviceSwitch")) && brandJsonConfig.getString("serviceSwitch").equals("0")) {
                flag = checkIsOutAge(brandId, date, 0);
            }
            AccountConfig accountConfig = accountConfigService.getByAccountId(accountId);
            if (accountConfig == null || !accountConfig.getServiceSwitch()) {
                flag = checkIsOutAge(accountId, date, 1);
            }
        } catch (Exception e) {
            log.error("校验是否停运异常", e);
        }
        return flag;
    }


    /**
     * 校验是否停运
     *
     * @param brandId 品牌或区域id
     * @param date    当天时间
     * @param type    0 品牌 1 区域
     */
    private Boolean checkIsOutAge(Integer brandId, Date date, Integer type) {
        OutAgeConfig outAgeConfig;
        if (0 == type) {
            outAgeConfig = cacheService.getOutAgeConfig(brandId, null);
        } else {
            outAgeConfig = cacheService.getOutAgeConfig(null, brandId);
        }

        if (Objects.isNull(outAgeConfig)) {
            throw new BaseException("运营商已停运，请检查停运配置");
        }
        //判断当前时间是否在停运时间内
        if (isTimeWithInRange(outAgeConfig.getShowdownStartTime(), outAgeConfig.getShowdownEndTime(), date) && StrUtil.isEmpty(outAgeConfig.getShowdownTimePeriod())) {
            return false;
        }
        if (StrUtil.isNotEmpty(outAgeConfig.getShowdownTimePeriod())) {
            for (String s : outAgeConfig.getShowdownTimePeriod().split(",")) {
                String[] split = s.split("-");
                String now = DateUtil.format(date, "yyyy-MM-dd");
                Date startDate = DateUtil.parse(now + " " + split[0]);
                Date endDate = DateUtil.parse(now + " " + split[1]);
                if (isTimeWithInRange(startDate, endDate, date)) {
                    return false;
                }
            }
        }
        return true;
    }


    /**
     * 判断当前时间是否在时间范围内
     *
     * @param startTime   开始时间
     * @param endTime     结束时间
     * @param currentTime 当前时间
     * @return boolean
     */
    private boolean isTimeWithInRange(Date startTime, Date endTime, Date currentTime) {
        // 判断当前时间是否在开始时间和结束时间之间
        return !currentTime.before(startTime) && !currentTime.after(endTime);
    }

    /**
     * 设备是否被预约
     */
    public void checkAppoint(Machine machine, Integer userId, AccountConfig accountConfig) {
        if (accountConfig != null && accountConfig.getAppoint() == 1) {
            AppointMent appointMent = appointMentDao.getByMachineId(machine.getMachineId());
            if (appointMent != null) {
                /**判断预约是否达到预约时长*/
                if (DateTimeUtil.getTimeDF(DateTimeUtil.getLastTime(DateTimeUtil.DateTimeToString(appointMent.getAppointmentTime()), accountConfig.getAppointTime() * 60),
                        DateTimeUtil.getNowTime()) >= 0) {
                    appointMentDao.del(appointMent.getUserId());
                } else {
                    /**非本人扫码，不能借车*/
                    if (!appointMent.getUserId().equals(userId)) {
                        throw new BaseException("-160004", "该车辆已被预约，请换一台车");
                    }

                }
            }

        }
    }

    /**
     * 借车
     */
    public Boolean borrowBikeNew(HttpServletRequest request, BorrowBikeVO vo, String serNO) {
        long startTime = System.currentTimeMillis();
        User user = vo.getUser();
        Machine machine = vo.getMachine();

        /** 找车时长逻辑处理,放入mq */
        String addTime = redisService.get(RedisConstant.USER_LOGIN_SIGN + user.getUserId());
        if (addTime != null) {
            ParkMachine parkMachine = parkMachineService.getByMachineId(machine.getMachineId());
            Integer parkPointId = null;
            if (parkMachine != null) {
                parkPointId = parkMachine.getParkPointId();
            }
            String software = request.getHeader("software");
            String appWare = request.getHeader("appWare");
            String phoneType = request.getHeader("phoneType");
            String way = request.getHeader("way");
            DataUtils.addFind(machine, user.getUserId(), parkPointId, addTime, software, appWare, phoneType, way,
                    vo.getOrderSource());
        }

        /** 兼容领导账号不需要购买 */
        LeaderAccount leaderAccount = leaderAccountDao.getByPhone(user.getPhone(), user.getAccountId());
        if (leaderAccount == null || System.currentTimeMillis() >= leaderAccount.getExpireTime().getTime()) {
            /** 判断是否支持免密支付 */
            /** 兼容借车存储实时头盔状态 */
            AccountConfigExt accountConfigExt = cacheService.getAccountConfigExtByKey(machine.getAccountId(),
                    AccountConfigExtConstant.ACCOUNTCONFIG_BORROW_ADD_EX);
            if (accountConfigExt != null && "1".equals(accountConfigExt.getParamValue())) {
                String serNO1 = UUID.randomUUID().toString();
                terControlService.sendControl(serNO1, machine.getMachineNO(), ControlTypeConstant.CONTROL_TYPE_GET,
                        ControlTypeConstant.CONTROL_TYPE_DEVICESTATUSEX, "", machine, false, -1, user.getUserId(),
                        "借车获取头盔状态");
                redisService.add(RedisConstant.MACHINE_HELMET_EX + machine.getMachineNO(), serNO, 60);
            }
        }

        /** 添加业务信息 */
        BorrowReturnTer info = new BorrowReturnTer();
        info.setType(0);
        info.setAccountId(machine.getAccountId());// 代理商的id
        info.setMachineId(machine.getMachineId());
        info.setUserId(user.getUserId());
        info.setMachineNO(machine.getMachineNO());
        info.setUserCode(machine.getUserCode());
        /** 添加用户和流水号绑定关系 */
        String oldNO = redisService.get("serNO" + user.getUserId());
        if (oldNO != null) {
            redisService.del(oldNO);
        }
        redisService.add("serNO" + user.getUserId(), serNO, 300);
        redisService.add(serNO, JSONObject.toJSONString(info), 300);

        /** 判断是否是GPRS通道 */
        Boolean ble = vo.getBle();
        if (!ble) {
            /** 给终端下发指令 */
            terControlService.sendControl(serNO, machine.getMachineNO(), ControlTypeConstant.CONTROL_TYPE_CONTROL,
                    ControlTypeConstant.CONTROL_TYPE_UNLOCK, "", machine, ble, vo.getOrderSource(), user.getUserId(),
                    "用户借车[GPRS]");
        } else {
            /** 记录日志信息 */
            OrderLog orderLog = new OrderLog();
            orderLog.setOrderSerNo(serNO);
            orderLog.setMachineId(machine.getMachineId());
            orderLog.setMachineNo(machine.getMachineNO());
            orderLog.setUserCode(machine.getUserCode());
            orderLog.setChannelType(
                    Integer.valueOf(ble ? ControlWay.CLIENT_BLE.getCode() : ControlWay.CLIENT_GPRS.getCode()));
            orderLog.setBusinessType(OrderLogTypeConstant.BUSINESSTYPE_BORROW);
            orderLog.setOpTime(DateTimeUtil.getNowTime());
            orderLog.setOpType(OrderLogTypeConstant.OPTYPE_USER);
            orderLog.setOrderContent(ControlTypeConstant.CONTROL_TYPE_UNLOCK);
            orderLog.setOrderSource(vo.getOrderSource());

            orderLog.setOrderType(ControlTypeConstant.CONTROL_TYPE_CONTROL);
            orderLog.setOpId(user.getUserId());
            orderLog.setRemark("用户借车" + (ble ? "[BLE]" : "[GPRS]"));
            orderLog.setAccountUserId(machine.getAccountId());
            orderLogDao.insert(orderLog);

            /** 蓝牙通道，创建订单 */
            log.info("[" + machine.getMachineNO() + "]使用蓝牙通道借车");
            ter_unlock(serNO);
            /** 记录用户发送的流水号 */
            redisService.add("rSerNo" + user.getUserId(), serNO, 60);
            /** 记录用户发送的时间 */
            redisService.add("rTime" + user.getUserId(), DateTimeUtil.getNowTime(), 60);
            /** 设备最后开锁时间 */
            redisService.add("MACHINELAST" + machine.getMachineNO(), DateTimeUtil.getNowTime(), 24 * 3600);
        }
        /** 借还车信息推送到mq */
        terControlService.send(machine.getAccountId(), user.getUserId(), machine.getMachineId(), vo.getOrderSource(),
                GatewayProtocol.MQ_MSG_ID_RSP_FAIL, MachineStateConstant.MACHINE_TYPE_BORROW,
                GatewayProtocol.MQ_MSG_ID_AROUND_BR_PUSH);
        log.info("借车下发指令时间[" + (System.currentTimeMillis() - startTime) + "]ms");

        return true;
    }

    @Override
    public BorrowBusMoney borrowBike(String serNO, User user, Integer accountId, Machine machine, Boolean ble, Integer orderSource, String borrowPosLogId
            , Integer useWxzff) {
        BorrowBusMoney borrowBusMoney = new BorrowBusMoney();

        long startTime = System.currentTimeMillis();
        AccountConfig accountConfig = accountConfigService.getByAccountId(machine.getAccountId());
        /**设备是否被预约*/
        checkAppoint(machine, user.getUserId(), accountConfig);

        if (machine.getState().equals(MachineStateConstant.MACHINE_STATE_GZ)) {
            throw new BaseException("-3029", "该车存在故障，请更换车辆");
        }
        if (machine.getState().equals(MachineStateConstant.MACHINE_STATE_DTF)) {
            throw new BaseException("-3012", "车辆未投放，请更换车辆");
        }

        if (!machine.getState().equals(MachineStateConstant.MACHINE_STATE_ZCYY)) {
            throw new BaseException("-3063", "不可租借车辆，请更换一辆车", terControlService, machine, user.getUserId(),
                    UseTimeConstant.OPEATE_BORROW, null, null, null, null);
        }

        /**判断设备是否在品牌下面*/
        Account accountAgent = accountService.getByAdAccountId(machine.getAccountId());
        if (!accountId.equals(accountAgent.getParentId())) {
            throw new BaseException("-30005", "该编号不存在");
        }
        /**小雨出行武汉大学开锁校验*/
        if (machine.getAccountId().equals(11416) && accountId.equals(348) && !user.getStudentAuth()) {
            xycxBorrowCheckService.checkBorrow(user.getIdNO(), user.getUserId());
        }

        // todo 运维占用，用户骑行使用数据库车辆状态定义
        /**判断是否是运维占用*/
        MachineOccupy machineOccupy = machineOccupyDao.getByMachineId(machine.getMachineId());
        if (machineOccupy != null) {
            throw new BaseException("-3001", "该车正在骑行");
        }

        /**查询当前车辆状态*/
		/*MachineBorrow machineBorrow=machineBorrowService.getByMachineId(machine.getMachineId());
		if(machineBorrow!=null){
			throw new BaseException("-3001","该车正在被骑行",terControlService,machine,user.getUserId(),UseTimeConstant.OPEATE_BORROW,null,null,null,null);
		}*/

        /**缓存是否存在借车信息,存在去数据库看下是否有重复数据*/
        String borrows = redisService.get(RedisConstant.MACHINE_RIDELOG + machine.getMachineId());
        if (borrows != null) {
            MachineBorrow machineBorrow = machineBorrowService.getByMachineId(machine.getMachineId());
            if (machineBorrow != null) {
                if (machineBorrow.getUserId().equals(user.getUserId())) {
                    return new BorrowBusMoney();
                }
                throw new BaseException("-3001", "该车正在被骑行", terControlService, machine, user.getUserId(),
                        UseTimeConstant.OPEATE_BORROW, null, null, null, null);
            } else {
                redisService.del(RedisConstant.MACHINE_RIDELOG + machine.getMachineId());
                redisService.del(RedisConstant.USER_RIDELOG + user.getUserId());
            }
        } else {
            MachineBorrow machineBorrow = machineBorrowService.getByMachineId(machine.getMachineId());
            if (machineBorrow != null) {
                if (machineBorrow.getUserId().equals(user.getUserId())) {
                    return new BorrowBusMoney();
                }
                throw new BaseException("-3001", "该车正在被骑行", terControlService, machine, user.getUserId(),
                        UseTimeConstant.OPEATE_BORROW, null, null, null, null);
            }
        }
        /** 查询车辆是否存在遗留停车记录 */
        Park park = parkService.getByMachineId(machine.getMachineId());
        if (park != null) {
            parkService.delByMachineId(machine.getMachineId());
        }

        /**区域开启人脸认证*/
        if (accountConfig != null && accountConfig.getFaceCheck() != null && accountConfig.getFaceCheck() == 1) {
            String tmp = redisService.get(RedisConstant.REDIS_USER_FACE + user.getUserId());
            if (tmp == null) {
                throw new BaseException("-30007", "请先人脸认证后在用车");
            }
        }
        /**兼容用户上报故障车辆是否可再次借出*/
        if ((accountConfig != null && accountConfig.getUserFaultLimit() == 1)) {
            MachineFault machineFault = machineFaultService.getByMachineId(machine.getMachineId());
            if (machineFault != null && machineFault.getFrom() == 1) {

                throw new BaseException("-3028", "车辆故障，请更换车辆骑行", terControlService, machine, user.getUserId(),
                        UseTimeConstant.OPEATE_BORROW, null, null, null, null);
            }
        }
        if (this.userFeedbackLimit(machine.getAccountId())) {
            MachineFault machineFault = machineFaultService.getByMachineId(machine.getMachineId());
            if (machineFault != null && machineFault.getFrom() == 3) {

                throw new BaseException("-3028", "车辆故障，请更换车辆骑行", terControlService, machine, user.getUserId(),
                        UseTimeConstant.OPEATE_BORROW, null, null, null, null);
            }
        }
        /**查询用户骑行次数*/
		/*UserSta userSta = userStaService.getByUserId(user.getUserId());
		Integer rideNum =0;
		if(userSta!=null&&userSta.getRideSums()!=null){
			rideNum = userSta.getRideSums();
		}*/
        Integer rideNum = 0;
        List<RideLog> rideLogs = rideLogService.getByUserId(user.getUserId());
        if (rideLogs.size() > 0) {
            rideNum = rideLogs.size();
        }
        /**判断是否已经实名认证*/
        /**兼0容区域代理不开启强制实名认证 兼容区域设置免实名认证次数*/
        if (accountConfig != null && accountConfig.getNameAuth() == 1 && (user.getNameAuth() == null || !user.getNameAuth()) && rideNum >= accountConfig.getNameAuthFreeTimes()) {
            throw new BaseException("-3024", "请先实名认证");
        }
        /**判断是否已经学生认证*/
        /**兼容区域代理开启强制学生认证*/
        if (accountConfig != null && accountConfig.getStudentAuth() == 1 && (user.getStudentAuth() == null || !user.getStudentAuth())) {
            throw new BaseException("-3040", "请先完成身份认证!");
        }
        /**通过人工实名认证途径认证后，用户在借车骑行时，将不再校验骑行年龄*/
        String remark = user.getRemark();
        if (remark == null || !remark.contains("人工认证")) {
            Set<String> targetConfigKeys = new HashSet<>();
            targetConfigKeys.add(CERTIFICATION_AGE_LIMIT_MIN_KEY);
            targetConfigKeys.add(CERTIFICATION_AGE_LIMIT_MAX_KEY);
            Map<String, String> configMap = accountConfigExtDao.selectByAccountIdAndKeys(machine.getAccountId(), targetConfigKeys).stream()
                    .collect(Collectors.toMap(AccountConfigExt::getParamKey, AccountConfigExt::getParamValue));
            int age = Optional.ofNullable(configMap.get(CERTIFICATION_AGE_LIMIT_MAX_KEY))
                    .map(Integer::parseInt)
                    .orElse(CERTIFICATION_MAX_AGE);
            int miniAge = Optional.ofNullable(configMap.get(CERTIFICATION_AGE_LIMIT_MIN_KEY))
                    .map(Integer::parseInt)
                    // 如果有自行车运营，则设置默认最小年龄为 12，否则 16
                    .orElse(Objects.equals(machine.getMachineType(), MachineStateConstant.MACHINE_TYPE_BIKE) ? CERTIFICATION_MIN_AGE_BIKE : CERTIFICATION_MIN_AGE_ELECTRIC_BIKE);

//            Integer age=65;
//            String value= cacheService.getBrandRemoteConfigValue(accountId,BrandConstant.CYCLING_AGE_LIMIT);
//            if(value!=null&&Integer.parseInt(value)>0){
//                age=Integer.parseInt(value);
//            }

            //兼容借车年龄需要自行车大于12岁、电动车大于16岁，小于65岁。兼容人工认证不是身份证号情况
            if (user.getIdNO() != null && user.getIdNO().length() >= 15 && AgeUtil.checkIsMan(user.getIdNO(), age)) {
                throw new BaseException("-3057", "用户年龄超出" + age + "周岁，为了您身体安全，限制用车");
            }

//            Integer miniAge=16;
//            if(Objects.equals(machine.getMachineType(), MachineStateConstant.MACHINE_TYPE_BIKE)){
//                miniAge=12;
//            }
            if (user.getIdNO() != null && user.getIdNO().length() >= 15 && !AgeUtil.checkIsMan(user.getIdNO(), miniAge)) {
                throw new BaseException("-3025", "未满" + miniAge + "周岁");
            }
        }
        /**判断是否有借出未还的车辆*/
        MachineBorrow machineBorrowUser = machineBorrowService.getByUserId(user.getUserId());
        if (machineBorrowUser != null) {
            throw new BaseException("-3005", "存在未还的车辆，请先还车后再借车", terControlService, machine, user.getUserId(),
                    UseTimeConstant.OPEATE_BORROW, null, null, null, null);
        }

        /**判断是否在黑名单*/
        UserBlackList userBlackList = userBlackListService.getByUserId(user.getUserId());
        if (userBlackList != null &&
                DateTimeUtil.getTimeDF(DateTimeUtil.getNowTime(), userBlackList.getExpireTime() + " 00:00:00") > 0) {
            throw new BaseException("-3036", "由于您未规范骑行，导致部分功能受限，如有疑问请联系客服协助解决", terControlService, machine, user.getUserId(),
                    UseTimeConstant.OPEATE_BORROW, null, null, null, null);
        }
        //UserBlackList userBlackListDB=userBlackListService.getByIdNO(user.getIdNO());
        List<UserBlackList> userBlackLists = userBlackListService.getByIdNO(user.getIdNO());
        if (userBlackLists.size() > 0) {
            for (UserBlackList userBlackListDB : userBlackLists) {
                if (userBlackListDB != null &&
                        DateTimeUtil.getTimeDF(DateTimeUtil.getNowTime(), userBlackListDB.getExpireTime() + " 00:00:00") > 0) {
                    throw new BaseException("-3036", "由于您未规范骑行，导致部分功能受限，如有疑问请联系客服协助解决", terControlService, machine, user.getUserId(),
                            UseTimeConstant.OPEATE_BORROW, null, null, null, null);
                }
            }
        }
        boolean isOpenWxZff = accountConfig != null && accountConfig.getWxZffMm() == 1;
        /**未开启微信支付分,并且用户押金状态是支付分免押*/
        if (!isOpenWxZff && user.getDepositState() == DepositStateConstant.DEPOSIT_STATE_SCORE) {
            /**用户提交押金金额大于0*/
            if (user.getDepositMoney() > 0) {
                userService.updateDeposit(user.getUserId(), DepositStateConstant.DEPOSIT_STATE_PAY);
                user.setDepositState(DepositStateConstant.DEPOSIT_STATE_PAY);
            } else {
                userService.updateDeposit(user.getUserId(), DepositStateConstant.DEPOSIT_STATE_NOTPAY);
                user.setDepositState(DepositStateConstant.DEPOSIT_STATE_NOTPAY);
            }
        }

        /**兼容领导账号不需要购买*/
        LeaderAccount leaderAccount = leaderAccountDao.getByPhone(user.getPhone(), user.getAccountId());
        if (leaderAccount == null || System.currentTimeMillis() >= leaderAccount.getExpireTime().getTime()) {
            AdAccountDeposit adAccountDeposit = adAccountDepositService.getByAccountId(machine.getAccountId());
            AgentDepositVip agentDepositVip = agentDepositVipService.getByUserId(user.getUserId(), machine.getAccountId());
            /**判断代理商是否要求购买免押套餐*/
            if (adAccountDeposit != null && adAccountDeposit.getDepositVip() == 1 && (user.getDepositState() != DepositStateConstant.DEPOSIT_STATE_PAY && user.getDepositState() != DepositStateConstant.DEPOSIT_STATE_ZMXY && user.getDepositState() != DepositStateConstant.DEPOSIT_STATE_VIRTUAL && user.getDepositState() != DepositStateConstant.DEPOSIT_STATE_SCORE) && rideNum >= adAccountDeposit.getFreeTimes()) {
                if (agentDepositVip == null) {
                    throw new BaseException("-3048", "请先购买免押套餐");
                }
                if (agentDepositVip != null && !DateTimeUtil.isDepositValid(agentDepositVip.getExpireDt())) {
                    throw new BaseException("-3048", "请先购买免押套餐");
                }
            }


            /**判断是否免押会员,免押会员期间不需要提交押金*/
            if ((agentDepositVip == null || !DateTimeUtil.isDepositValid(agentDepositVip.getExpireDt()))
                    && adAccountDeposit != null && rideNum >= adAccountDeposit.getFreeTimes()) {
                /**判断是是否学生认证，学生认证了，不需要提交押金*/
                /**兼容虚拟押金功能，押金状态为虚拟押金也不用提交押金*/
                if (user.getDepositState() != DepositStateConstant.DEPOSIT_STATE_STUDENT && user.getDepositState() != DepositStateConstant.DEPOSIT_STATE_ZMXY &&
                        user.getDepositState() != DepositStateConstant.DEPOSIT_STATE_VIRTUAL && user.getDepositState() != DepositStateConstant.DEPOSIT_STATE_SCORE
                        && (!isOpenWxZff || useWxzff == 0)) {
                    /**查询用户是否提交押金*/
                    if (user.getDepositState() == DepositStateConstant.DEPOSIT_STATE_RETURNING) {
                        throw new BaseException("-3003", "押金退还中,不能借车");
                    }
                    if (user.getDepositState() == DepositStateConstant.DEPOSIT_STATE_FROZEN) {
                        throw new BaseException("-3033", "押金冻结，不能借车");
                    }

                    if (adAccountDeposit == null) {
                        throw new BaseException("-3010", "押金未定义");
                    }

                    /**未提交押金*/
                    if (user.getDepositState() == 0 && adAccountDeposit.getDeposit() > 0) {
                        Integer depositMoney = adAccountDeposit.getDeposit();
                        return new BorrowBusMoney((double) -depositMoney, null);
                    }

                    /**需要补交押金*/
                    if ((user.getDepositState() == DepositStateConstant.DEPOSIT_STATE_PAY) && (adAccountDeposit.getDeposit() > user.getDepositMoney())) {
                        Double depositMoney = adAccountDeposit.getDeposit() - user.getDepositMoney();
                        return new BorrowBusMoney(depositMoney, null);
                    }
                }
            }
            AdAccountFee adAccountFee = adAccountFeeService.getByAccountId(machine.getAccountId(), machine.getMachineId());
            if (adAccountFee == null) {
                throw new BaseException("-3056", "计费规则未配置");
            }
            // 判断是否卡券用户 以及 是否开启免密
            if (!isOpenWxZff && !noLimitChargeValid(user.getUserId(), machine.getAccountId())) {
                /**判断是否要求预充值*/
                if (adAccountFee != null && adAccountFee.getNewUserRechcrge() > 0) {
                    Integer rideLogCount = rideNum;
                    //Integer rideLogCount=userSta.getRideTimes();
                    Double needRechargeMoney = adAccountFee.getNewUserRechcrge() - user.getMoney();
                    if (rideLogCount == 0 && needRechargeMoney > 0) {
                        // 首充金额
                        return new BorrowBusMoney(null, needRechargeMoney);
                    }
                }
                Integer giftCardMoney = 0;
                /**礼品卡余额*/
                GiftCardUser giftCardUser = giftCardUserService.getByUserId(user.getUserId(), machine.getAccountId());
                if (giftCardUser != null) {
                    giftCardMoney = giftCardUser.getMoney();
                }
                /**兼容支付分订单*/
                if (adAccountFee.getRechargeBase() > 0 && (giftCardMoney + user.getMoney()) < adAccountFee.getRechargeBase()) {
                    // 骑行最低金额
                    return new BorrowBusMoney((double) adAccountFee.getRechargeBase());
                }
            }

            /**兼容借车存储实时头盔状态*/
            AccountConfigExt accountConfigExt = cacheService.getAccountConfigExtByKey(machine.getAccountId(), AccountConfigExtConstant.ACCOUNTCONFIG_BORROW_ADD_EX);
            if (accountConfigExt != null && "1".equals(accountConfigExt.getParamValue())) {
                String serNO1 = UUID.randomUUID().toString();
                terControlService
                        .sendControl(serNO1, machine.getMachineNO(), ControlTypeConstant.CONTROL_TYPE_GET,
                                ControlTypeConstant.CONTROL_TYPE_DEVICESTATUSEX, "", machine, false, -1,
                                user.getUserId(), "借车获取头盔状态");
                redisService.add(RedisConstant.MACHINE_HELMET_EX + machine.getMachineNO(), serNO, 60);
            }
            //头盔校验
            boolean isHelmet = Optional.ofNullable(cacheService.getAccountConfigExtByKey(machine.getAccountId(),
                            AccountConfigExtConstant.ACCOUNTCONFIG_HELMET)).map(configExt -> "1".equals(configExt.getParamValue()))
                    .orElse(false);
            Map<String, Object> param = new HashMap<>();
            param.put("machineId", machine.getMachineId());
            param.put("functionType", MachineStateConstant.MACHINE_TYPE_BLE_HELMET);
            MachineFunction function = machineFunctionDao.getByMachineId(param);
            if (function != null) {
                BleHelmet bleHelmet = bleHelmetService.getByUserCode(machine.getUserCode());
                if (bleHelmet != null) {
                    String tmp = redisService.get(RedisConstant.RIDELOG_BEFORE_BORROW_STATUS + machine.getMachineNO());
                    if ((isHelmet && (tmp == null || !ObjectUtil
                            .statusParse(0, Integer.valueOf(tmp))))) {
                        throw new BaseException("-140002", "头盔锁故障,请换一辆车骑行");
                    }
                }
            } else {
                String json = redisService.getMain(RedisUtil.getTerHashId(machine.getMachineNO()), RedisConstant.REDIS_TER_LAST_STATUS);
                if ((json != null && json.length() > 0)) {
                    TerLastStatus terLastStatus = JSON.parseObject(json, TerLastStatus.class);
                    Integer terStatus = terLastStatus.getCarStatus();
                    /**头盔锁缓存*/
                    redisService.addMain(
                            RedisConstant.MACHINE_NO_HELMET + user.getUserId(), terStatus.toString(), 24 * 60 * 60);
                    //检查车辆是否配置了其它头盔锁功能，如果没配置则不校验
                    boolean haveOtherHelmetFuntion = this.checkHaveOtherHelmetFuntion(machine.getMachineId(),
                            new ArrayList<>(Arrays.asList(MachineStateConstant.MACHINE_TYPE_HELMAT, MachineStateConstant.MACHINE_TYPE_INTEL_HELMET)));
                    if (isHelmet && ObjectUtil
                            .statusParse(11, Integer.valueOf(terStatus)) && haveOtherHelmetFuntion) {
                        throw new BaseException("-140002", "头盔锁故障,请换一辆车骑行");
                    }
                } else {
                    log.warn(machine.getMachineNO() + "未获取到车辆状态");
                }
            }

            //开启支付分，但是没有创建支付分订单，需要返回错误码，让小程序创建
            if (isOpenWxZff && rechargeService.getByWxzzfNotPay(user.getUserId(), RechargeConstant.RECHARGE_WEIXIN_AFF) == null && useWxzff == 1) {
                throw new BaseException("-70002", "支付分订单未创建");
            }

        }

        /**添加业务信息*/
        BorrowReturnTer info = new BorrowReturnTer();
        info.setType(0);
        info.setAccountId(machine.getAccountId());//代理商的id
        info.setMachineId(machine.getMachineId());
        info.setUserId(user.getUserId());
        info.setMachineNO(machine.getMachineNO());
        info.setUserCode(machine.getUserCode());
        info.setBorrowPosLogId(borrowPosLogId);
        /**添加用户和流水号绑定关系*/
        String oldNO = redisService.get("serNO" + user.getUserId());
        if (oldNO != null) {
            redisService.del(oldNO);
        }
        redisService.add("serNO" + user.getUserId(), serNO, 300);
        redisService.add(serNO, JSONObject.toJSONString(info), 300);

        /**体重检测*/
        if (userDetailService.shouldLogWeight(machine.getAccountId(), machine.getMachineId())) {

            UserDetail userDetail = userDetailService.get(0, user.getUserId());

            if (userDetail != null && StrUtil.isNotBlank(userDetail.getValue())) {
                MachineFunction machineWeightFunction = machineFunctionService.get(machine.getMachineId(), MachineStateConstant.MACHINE_TYPE_WEIGHT);

                String weightParam = String.format("USERSTANDARDWEIGHT=%s-%s;", userDetail.getValue(), machineWeightFunction.getFunctionMode());

                terControlService.sendControl(UUID.randomUUID().toString(), machine.getMachineNO(), ControlTypeConstant.CONTROL_TYPE_SET, weightParam, "", machine, ble, orderSource, user.getUserId(), "开锁同步用户体重");
            }
        }

        /**判断是否是GPRS通道*/
        if (!ble) {
            // 根据 区域配置 , 车辆功能配置 决定
            AccountConfigExt accountConfigExt = cacheService.selectByAccountIdAndKey(machine.getAccountId(),AccountConfigExtConstant.XIAO_AN_HELMET);
            boolean xiaoAn_HelmetSwitch = (Objects.nonNull(accountConfigExt) && Objects.equals(accountConfigExt.getParamValue(),"1"));
            List<MachineFunction> allFunction = Optional.ofNullable(cacheService.getMachineFunction(machine.getMachineId())).orElse(Collections.emptyList());
            boolean helmetWear = allFunction.stream().anyMatch(x -> Objects.equals(x.getFunctionType(), MachineFunctionConstant.HELMET_WEAR) && Objects.equals(x.getFunctionValue(), 1));
            log.info("小安头盔调试信息,xiaoAn_HelmetSwitch={},helmetWear={}",xiaoAn_HelmetSwitch,helmetWear);
            if (xiaoAn_HelmetSwitch && helmetWear){
                terControlService.sendControl(serNO, machine.getMachineNO(), ControlTypeConstant.CONTROL_TYPE_CONTROL, ControlTypeConstant.CONTROL_TYPE_UNLOCK, "helmetWear", machine, ble, orderSource, user.getUserId(), "用户借车[GPRS]");
            }else {
                /**给终端下发指令*/
                terControlService.sendControl(serNO, machine.getMachineNO(), ControlTypeConstant.CONTROL_TYPE_CONTROL, ControlTypeConstant.CONTROL_TYPE_UNLOCK, "", machine, ble, orderSource, user.getUserId(), "用户借车[GPRS]");
            }
            //自行车触发同步静止临停时间
            if (MachineStateConstant.MACHINE_TYPE_BIKE.equals(machine.getMachineType()) && accountConfig.getStopTime() > 0 && accountConfig.getFinishOrderSwitch()) {
                terControlService.sendControl(UUID.randomUUID().toString(), machine.getMachineNO(), ControlTypeConstant.CONTROL_TYPE_SET, "DFTAUTIOMATICLOCK=" + accountConfig.getStopTime() + ";", "", machine, ble, orderSource, user.getUserId(), "开锁同步临停时间");
            }

        } else {
            /**记录日志信息 */
            OrderLog orderLog = new OrderLog();
            orderLog.setOrderSerNo(serNO);
            orderLog.setMachineId(machine.getMachineId());
            orderLog.setMachineNo(machine.getMachineNO());
            orderLog.setUserCode(machine.getUserCode());
            orderLog.setChannelType(Integer.valueOf(ble ? ControlWay.CLIENT_BLE.getCode() : ControlWay.CLIENT_GPRS.getCode()));
            orderLog.setBusinessType(OrderLogTypeConstant.BUSINESSTYPE_BORROW);
            orderLog.setOpTime(DateTimeUtil.getNowTime());
            orderLog.setOpType(OrderLogTypeConstant.OPTYPE_USER);
            orderLog.setOrderContent(ControlTypeConstant.CONTROL_TYPE_UNLOCK);
            orderLog.setOrderSource(orderSource);

            orderLog.setOrderType(ControlTypeConstant.CONTROL_TYPE_CONTROL);
            orderLog.setOpId(user.getUserId());
            orderLog.setRemark("用户借车" + (ble ? "[BLE]" : "[GPRS]"));
            orderLog.setAccountUserId(machine.getAccountId());
            orderLog.setRspDt(DateTimeUtil.getNowTime());
            orderLog.setRspRet(1);
            orderLog.setRspContent("1");
            orderLogDao.addBle(orderLog);
        }
        /**借还车信息推送到mq*/
        terControlService.send(machine.getAccountId(), user.getUserId(), machine.getMachineId(), orderSource,
                GatewayProtocol.MQ_MSG_ID_RSP_FAIL, MachineStateConstant.MACHINE_TYPE_BORROW,
                GatewayProtocol.MQ_MSG_ID_AROUND_BR_PUSH);


        log.info("借车下发指令时间[" + (System.currentTimeMillis() - startTime) + "]ms");

        return borrowBusMoney;
    }

    /**
     * 是否免除骑行最低金额校验/首充金额校验
     * 判断用户是否存在以下权益
     * 骑行卡 0，会员卡 1，免押卡 2
     *
     * @param userId
     * @param accountId 区域id
     * @return
     */
    private boolean noLimitChargeValid(Integer userId, Integer accountId) {
        String value = Optional.ofNullable(cacheService.getAccountConfigExtByKey(accountId, AccountConfigExtConstant.NO_LIMIT_CHARGE_CARD_TYPE)).orElse(new AccountConfigExt(accountId, AccountConfigExtConstant.NO_LIMIT_CHARGE_CARD_TYPE, null, new Date(), null)).getParamValue();
        if (Objects.isNull(value) || Objects.equals(value, "")) {
            return false;
        }
        return (value.contains("0") && rideCardUserService.hasValidRideCard(userId))
                || (value.contains("1") && vipCardLogService.hasValidVipCard(userId))
                || (value.contains("2") && memberLogService.hasValidCard(userId));
    }

    @Override
    @Transactional
    public void ter_unlock(String serNO) {
        long startTime = System.currentTimeMillis();

        String brtInfo = redisService.get(serNO);

        if (brtInfo != null) {
            BorrowReturnTer info = JSONObject.parseObject(brtInfo, BorrowReturnTer.class);
            if (info.getType() == 0) {//正常接还车k
                /**骑行订单号*/
                String orderNO = StringUtil.getRideLogOrderNO();

                /**判断是否已经借出*/
                MachineBorrow machineBorrowDB = machineBorrowService.getByMachineId(info.getMachineId());
                if (machineBorrowDB == null) {
                    /**添加到最后借出表中*/
                    MachineBorrow machineBorrowAdd = new MachineBorrow();
                    machineBorrowAdd.setOrderNO(orderNO);
                    machineBorrowAdd.setUserId(info.getUserId());
                    machineBorrowAdd.setMachineId(info.getMachineId());
                    machineBorrowAdd.setCreateTime(new Date());
                    machineBorrowService.add(machineBorrowAdd);

                    /**添加到骑行记录表中*/
                    RideLog rideLog = new RideLog();
                    rideLog.setOrderNO(orderNO);
                    rideLog.setAccountId(info.getAccountId());
                    rideLog.setMachineId(info.getMachineId());
                    rideLog.setStartTime(DateTimeUtil.getNowTime());
                    rideLog.setUserId(info.getUserId());
                    rideLog.setMachineNO(info.getMachineNO());
                    rideLog.setUserCode(info.getUserCode());
                    /**兼容站点借出数据统计功能，记录车辆解除站点*/
                    ParkMachine parkMachine = parkMachineService.getByMachineId(info.getMachineId());
                    if (parkMachine != null) {
                        rideLog.setStartPointId(parkMachine.getParkPointId());
                    }
                    rideLogService.insert(rideLog);
                    /**区域用户绑定*/
                    userToAgentService.insert(info.getUserId(), info.getAccountId());


                    ParkMachine parkMachineDB = new ParkMachine();
                    parkMachineDB.setInside(0);
                    parkMachineDB.setMachineId(info.getMachineId());
                    parkMachineService.edit(parkMachineDB);
                    /**更新设备开关锁记录*/

                    /**更新车辆最后状态*/
                    updateLastStatus(info.getMachineId(), info.getUserId());
                    /** 记录用户借车 */
                    SystemData.userRentTime.put("rTime" + info.getUserId(), DateTimeUtil.getNowTime());
                    log.info("用户" + info.getUserId() + "借车时间： " + DateTimeUtil.getNowTime());

                    //更新借车记录,记录订单号
                    updateBorrowPosLog(info, orderNO);

                    /**清除redis数据*/
                    redisService.del(RedisConstant.USER_OPEN_HELMET + info.getMachineId());

                    User user = userService.getByUserId(info.getUserId());
                    AdAccountFee adAccountFee = adAccountFeeService.getByAccountId(info.getAccountId(), info.getMachineId());
                    AccountConfig accountConfig = accountConfigService.getByAccountId(info.getAccountId());
                    /**判断是否支持免密支付*/
                    /*if (accountConfig != null && accountConfig.getWxZffMm() == 1) {
                        UserWxzff userWxzff = userWxzffDao.getByUserId(info.getUserId());
                        if (userWxzff != null && userWxzff.getState() == 1) {
                            Integer money = 3000;
                            *//**风险金额暂时使用超区金额*//*
                            if (adAccountFee.getAreaMoney() > 0) {
                                money = adAccountFee.getAreaMoney();
                            }
                            payPointService.wakeUpPaymentPoints(user.getAccountId(), info.getUserId(), money, userWxzff.getOpenId(), info.getAccountId(), adAccountFee);
                        }
                    }*/
                    /**借还车电量信息存放内存，定时推送*/
                    pushVolMsg(info, rideLog.getOrderNO(), MachineStateConstant.MACHINE_TYPE_BORROW);
                    /**删除用户记录*/
                    redisService.del("rSerNo" + info.getUserId());
                    redisService.del("rTime" + info.getUserId());
                    /**redis借车内存信息*/
                    redisService.add(
                            RedisConstant.USER_RIDELOG + user.getUserId(), JSON.toJSONString(rideLog), 24 * 3600);
                    redisService.add(RedisConstant.MACHINE_RIDELOG + machineBorrowAdd.getMachineId(), JSON.toJSONString(rideLog), 24 * 3600);
                    if (accountConfig != null && accountConfig.getAppoint() == 1) {
                        /**删除预约信息*/
                        appointMentDao.del(info.getUserId());
                    }

                    /**借车信息推送mq到数据推送平台*/
                    String pointMsg = redisService.get(RedisConstant.MACHINE_BORROW_LOCAL + info.getMachineNO());
                    if (pointMsg != null) {
                        Point point = JSON.parseObject(pointMsg, Point.class);
                        String bleMsg = redisService.get(RedisConstant.MACHINE_BLE + info.getMachineNO());
                        String rfidMsg = redisService.get(RedisConstant.MACHINE_RFID + info.getMachineNO());
                        RidePushEntity ridePushEntity = new RidePushEntity(orderNO, info.getUserCode(), info.getMachineNO(), bleMsg, rfidMsg, info.getAccountId(), user.getPhone(),
                                point.getX(), point.getY(), rideLog.getStartTime(), null, null, 0, null, null);
                        MqData<RidePushEntity> mqData = new MqData<>();
                        mqData.setData(ridePushEntity);
                        mqData.setMsgId(GatewayProtocol.MQ_MSG_ID_AROUND_RIDE_PUSH);
                        mqData.setFeedback(RabbitMQData.ridequeue);
                        terControlService.sendPushMsg(RabbitMQData.ridekey, JSON.toJSONString(mqData));
                    }
                    verticalParkLogService.editEnable(info.getMachineNO());

                    // 插入车辆操作日志
                    Machine machine = machineService.getByMachineId(info.getMachineId());
                    machineOpLogService.insertSingle(MachineOpLog.builder()
                            .accountId(machine.getAccountId())
                            .machineId(machine.getMachineId())
                            .machineNO(machine.getMachineNO())
                            .userCode(machine.getUserCode())
                            .operation("借车")
                            .opUser(user.getName())
                            .opUserPhone(user.getPhone())
                            .opPlatform(OperationPlatform.CLIENT.code)
                            .result(true)
                            .detail("指令流水号: " + serNO + "\n骑行单号: " + orderNO)
                            .opTime(LocalDateTime.now())
                            .build());
                } else {
                    log.warn(info.getMachineNO() + ",借车失败，车辆已经被借出");
                }
            } else if (info.getType() == 1) {//中途停车
                /**查询停车记录*/
                Park park = parkService.getByUserId(info.getUserId());
                if (park != null) {
                    /**计算停车时长*/
                    String endTime = DateTimeUtil.getNowTime();
                    Integer stopTime = DateTimeUtil.getTime(park.getStartTime(), endTime);
                    log.info("临时停车时长=" + info.getOrderNO() + "," + stopTime + "s");

                    /**更新停车时长*/
                    if (stopTime > 0) {
                        rideLogService.updateStopTimeByOrderNO(info.getOrderNO(), stopTime);
                    }

                    /**删除中途停车记录*/
                    parkService.delByUserId(info.getUserId());

                    /**记录停车日志*/
                    park.setEndTime(endTime);
                    parkService.addLog(park);
                } else {
                    log.warn("中途停车信息不存在");
                }
            }
        } else {
            log.warn("借车业务信息不存在");
        }

        log.info("开锁业务处理时间[" + (System.currentTimeMillis() - startTime) + "]ms");
    }

    private int updateBorrowPosLog(BorrowReturnTer borrowReturnTer, String orderNO) {
        String borrowPosLogIdStr = borrowReturnTer.getBorrowPosLogId();
        if (StringUtils.isBlank(borrowPosLogIdStr)) {
            return 0;
        }
        Long borrowPosLogId = Long.valueOf(borrowPosLogIdStr);
        return borrowPosLogDao.updateOrderNoById(borrowPosLogId, orderNO);
    }


    /**
     * 还车
     *
     * @param serNO        流水号
     * @param userId       用户id
     * @param ble          是否蓝牙
     * @param point        坐标
     * @param dispatchType 调度类型
     * @param auto         自动
     * @param orderSource  订单来源：安卓？其它
     * @param mapType      地图类型，默认谷歌
     * @param lang         语言
     * @param secondType   第二调度类型
     * @return
     */
    @Override
    public Double returnBike(String serNO, Integer userId, Boolean ble, Point point, Integer dispatchType,

                             Boolean auto, Integer orderSource, Integer mapType, String lang, String ip, Integer secondType) {

        long startTime = System.currentTimeMillis();
        /**需要充值支付的金额*/
        Double money = null;
        /**订单信息**/
        RideLog rideLog = Optional.ofNullable(rideLogService.getByUserIdNotFinish(userId))
                .orElseThrow(() -> new BaseException("-3006", "骑行订单已经结束"));
        /**车辆信息**/
        Machine machine = Optional.ofNullable(cacheService.getMachineInfo(rideLog.getMachineId(), null, null))
                .orElseThrow(() -> new BaseException("-30005", "车辆不存在"));
        /**用户id**/
        Integer userid = rideLog.getUserId();

        /**用户信息**/
        User user = userService.getByUserId(userid);
        /**区域配置**/
        AccountConfig accountConfig = accountConfigService.getByAccountId(machine.getAccountId());


        Park park = parkService.getByUserId(user.getUserId());

        Map<String, String> map = new HashMap<>();

        Integer afterPay = Optional.ofNullable(accountConfig).map(AccountConfig::getAfterPay)
                .orElseGet(() -> AccountConfigExtConstant.ACCOUNTCONFIG_AFTERPAY_CLOSE);
        money = getMoney(user, rideLog, park, dispatchType, serNO, map, 1, null,
                null, null, null, secondType, afterPay);
        Double allMoney = Double.parseDouble(map.get("allMoney"));
        String endTime = map.get("endTime");

        /**可使用微信支付分**/
        Integer wxZffMm = Optional.ofNullable(accountConfig).map(AccountConfig::getWxZffMm).orElseGet(() -> 0);


        /**支付分支付*/
        Boolean isWxzff = false;
        if (wxZffMm == 1) {
            String disMoney = map.get("dispatchMoney");
            Integer disPatchMoney = disMoney == null ? null : Double.valueOf(disMoney).intValue();
            boolean zffResult = payPointService.finishOrderOut(user, money, serNO, rideLog.getAccountId()

                    , DateTimeUtil.getHourMinuteSecond(rideLog.getStartTime(), endTime), disPatchMoney, "骑行扣费");
            if (zffResult) {
                isWxzff = true;
                money = 0D;
            } else {
                /**异常订单0元取消*/
                Boolean result = payPointService.cancelOrder(user, "扣款失败转手工充值");

                String brtInfo = redisService.get(serNO);
                BorrowReturnTer info = JSONObject.parseObject(brtInfo, BorrowReturnTer.class);
                info.setLessMoney(money.intValue());

                //cash(info);
            }

        }

        /**微信支付分支付,不需要在付费*/
        if (!isWxzff) {
            /**需要充值的金额不为null,并且不是支付分支付,未打开后付费开关*/
            if (money > 0 && afterPay.equals(AccountConfigExtConstant.ACCOUNTCONFIG_AFTERPAY_CLOSE)) {
                return money;
            }
            /**后付费，不返回金额给小程序*/
            if (afterPay.equals(AccountConfigExtConstant.ACCOUNTCONFIG_AFTERPAY_OPEN)) {
                money = 0D;
            }
            if (park == null) {
                /**金额足够支付,直接结束订单*/
                if (user.getMoney() >= allMoney) {
                    if (allMoney > 0) {
                        Integer giftCardMoney = map.get("giftCardMoney") == null ? 0 : Integer.valueOf(map.get("giftCardMoney"));
                        /**分账校验*/
                        log.info("还车记录区域,userId={},accountId={},orderNO={}", userId, machine.getAccountId(), rideLog.getOrderNO());
                        delay(allMoney, user.getMoney(), giftCardMoney, 0, machine.getAccountId(),
                                user, rideLog.getOrderNO(), accountConfig, serNO);
                    }
                    /**自动结束订单开关打开*/
                    /**&&MachineStateConstant.MACHINE_TYPE_ELECT_BIKE.equals(machine.getMachineType())*/
                    if (accountConfig != null && accountConfig.getAutoLock() == 1 && MachineStateConstant.MACHINE_TYPE_ELECT_BIKE.equals(machine.getMachineType())) {
                        redisService.add(RedisConstant.REDIS_TER_PAY_FINISHORDER_SERNO + serNO, serNO, 60);
                        /**添加自动结束订单标志，防止出现多次扣费*/
                        redisService.add(RedisConstant.REDIS_PAY_SIGN + serNO, "1", 10);
                        /**添加自动结束订单标志，防止出现多次扣费*/
                        ter_lock(serNO);
                        log.warn(user.getUserId() + "自动结束订单，订单编号=" + rideLog.getOrderNO());
                        /**记录日志*/
                        VerticalLog verticalLog = SystemData.verticalMap.get(rideLog.getOrderNO());
                        if (verticalLog != null) {
                            log.warn("订单编号=" + rideLog.getOrderNO() + "结束订单，还车信息=" + verticalLog);
                            SystemData.verticalMap.remove(rideLog.getOrderNO());
                        }
                    }
                }
            } else {
                /**删除中途停车记录*/
                parkService.delByUserId(user.getUserId());
                /**记录停车日志*/
                park.setEndTime(endTime);
                parkService.addLog(park);
                log.warn(machine.getMachineNO() + "临时停车直接还车，订单编号=" + rideLog.getOrderNO());
                /**扣费*/
                ter_lock(serNO);
                /**是否需要分账校验*/
                if (allMoney > 0 && user.getMoney() >= allMoney) {
                    log.info("还车记录区域,userId={},accountId={},orderNO={}", userId, machine.getAccountId(), rideLog.getOrderNO());
                    delay(allMoney, user.getMoney(), 0, 0, machine.getAccountId(), user, rideLog.getOrderNO(), accountConfig, null);
                }
                /**记录日志*/
                VerticalLog verticalLog = SystemData.verticalMap.get(rideLog.getOrderNO());
                if (verticalLog != null) {
                    log.warn("订单编号=" + rideLog.getOrderNO() + "结束订单，还车信息=" + verticalLog);
                    SystemData.verticalMap.remove(rideLog.getOrderNO());
                }
            }
            /**判断是不是GPRS通道*/
            if (!ble) {
                /**给终端下发指令*/
                terControlService.sendControl(serNO, machine.getMachineNO(), ControlTypeConstant.CONTROL_TYPE_CONTROL, ControlTypeConstant.CONTROL_TYPE_LOCK, "", machine, ble, orderSource, userId, "用户还车[GPRS]");
            } else {
                /**记录日志信息 */
                OrderLog orderLog = new OrderLog();
                orderLog.setOrderSerNo(serNO);
                orderLog.setMachineId(machine.getMachineId());
                orderLog.setMachineNo(machine.getMachineNO());
                orderLog.setUserCode(machine.getUserCode());
                orderLog.setChannelType(Integer.valueOf(ble ? ControlWay.CLIENT_BLE.getCode() : ControlWay.CLIENT_GPRS.getCode()));
                orderLog.setBusinessType(OrderLogTypeConstant.BUSINESSTYPE_RETURN);
                orderLog.setOpTime(DateTimeUtil.getNowTime());
                orderLog.setOpType(OrderLogTypeConstant.OPTYPE_USER);
                orderLog.setOrderContent(ControlTypeConstant.CONTROL_TYPE_LOCK);
                orderLog.setOrderSource(orderSource);

                orderLog.setOrderType(ControlTypeConstant.CONTROL_TYPE_CONTROL);
                orderLog.setOpId(user.getUserId());
                orderLog.setRemark("用户还车" + (ble ? "[BLE]" : "[GPRS]"));
                orderLog.setAccountUserId(machine.getAccountId());
                orderLog.setRspDt(DateTimeUtil.getNowTime());
                orderLog.setRspRet(1);
                orderLog.setRspContent("1");
                orderLogDao.addBle(orderLog);
            }
            log.info("订单编号=" + rideLog.getOrderNO() + "还车下发指令时间[" + (System.currentTimeMillis() - startTime) + "]ms");

            // 插入车辆操作日志
            String detail = "骑行单号: " + rideLog.getOrderNO() + "\n指令流水号: " + serNO;
            machineOpLogService.insertSingle(MachineOpLog.builder()
                    .accountId(machine.getAccountId())
                    .machineId(machine.getMachineId())
                    .machineNO(machine.getMachineNO())
                    .userCode(machine.getUserCode())
                    .operation("关锁还车")
                    .opType("用户还车")
                    .opUser(user.getName())
                    .opUserPhone(user.getPhone())
                    .opPlatform(OperationPlatform.CLIENT.code)
                    .result(true)
                    .detail(detail)
                    .opTime(LocalDateTime.now())
                    .build()
            );
            /**可以还车，清除还车角度校验失败缓存数据*/
            redisService.del(RedisConstant.ANGLE_FAIL_KEY_PREFIX + machine.getMachineId());
        }
        return money;
    }


    /**
     * 计算费用
     *
     * @param user         用户信息
     * @param rideLog      骑行订单信息
     * @param park         临时停车信息
     * @param dispatchType 调度类型
     * @param serNO        流水号
     * @param map          返回集合
     * @param secondType   第二调度类型
     * @return
     */
    @Override
    public Double getMoney(User user, RideLog rideLog, Park park, Integer dispatchType, String serNO, Map<String,

                                   String> map, Integer returnType, Integer managerMoney, Integer operatorId, String operatorName,
                           String operatorPhone, Integer secondType, Integer afterPay) {

        boolean isArtificial = true;
        /**2为人工结束订单,不享受任何优惠*/
        if (returnType == 2) {
            isArtificial = false;
        }
        Double money = 0D;
        Machine machine = machineDao.getByMachineId(rideLog.getMachineId());
        /**用户手机号**/
        String userphone = user.getPhone();
        Account account = accountService.getByAccountId(machine.getAccountId());

        /**用户还车站点缓存**/
        String endPark = redisService.get(RedisConstant.USER_PARKPOINTID + rideLog.getOrderNO());
        /**用户还车禁停区缓存**/
        String prohibitareaId = redisService.get(RedisConstant.USER_PROHIBITAREAID + rideLog.getOrderNO());
        /**调度车辆**/
        Integer parkPointId = Optional.ofNullable(endPark).map(s -> Integer.valueOf(endPark)).orElseGet(() -> -1);
        /**获取区域计费规则**/
        //AdAccountFee adAccountFee = adAccountFeeService.getByAccountId(machine.getAccountId(), machine.getMachineId());
        // 节假日计费
        AdAccountFee adAccountFee = adAccountFeeService.getFee(machine.getAccountId(), machine.getMachineId(), rideLog.getStartTime());
        /**每日前N分钟免费骑行的分钟数*/
        Integer freeTime = Optional.ofNullable(adAccountFee).map(o -> o.getFreeTime()).orElseGet(() -> 0);
        /**每日前N分钟免费骑行次数*/
        Integer accountFreeTime = Optional.ofNullable(adAccountFee).map(o -> o.getFreeRideTime()).orElseGet(() -> 0);
        /**每次前N米免费骑行距离*/
        Integer freeDistance = Optional.ofNullable(adAccountFee).map(o -> o.getFreeDistance()).orElseGet(() -> 0);
        /**免费骑行满足条件 0:满足全部启用条件 1:满足任一启用条件*/
        Integer freeConditionType = Optional.ofNullable(adAccountFee).map(o -> o.getFreeConditionType()).orElseGet(() -> 0);
        /**新用户优惠：每次免费骑行时长**/
        Integer newUserFreeTime = Optional.ofNullable(adAccountFee).map(o -> o.getNewUserFreeTime()).orElseGet(() -> 0);
        /**新用户优惠：免费骑行次数**/
        Integer newUserFreeTimes = Optional.ofNullable(adAccountFee).map(o -> o.getNewUserFreeTimes()).orElseGet(() -> 0);
        /**计时封顶金额**/
        Integer capTimeFee = Optional.ofNullable(adAccountFee).map(o -> o.getCapTimeFee()).orElseGet(() -> 0);
        /**里程封顶金额**/
        Integer capMileFee = Optional.ofNullable(adAccountFee).map(o -> o.getCapMileFee()).orElseGet(() -> 0);
        /**计费规则id*/
        Integer feeId = Optional.ofNullable(adAccountFee).filter(o -> null != o.getName()).map(o -> o.getFeeId()).orElseGet(() -> 0);
        /**调度费用**/

        DisPatchData disPatchData = new DisPatchData();
        Double dispatchMoney = getDispatchMoney(dispatchType, adAccountFee, secondType, disPatchData);
        /**头盔丢失记录*/
        //如果开启了头盔丢失检查并且头盔丢失了就记录一条头盔丢失日志
        if (secondType != null && Objects.equals(secondType, DisPatchConstant.DISPATCH_HELMET)
                && this.helmetLostCheckSwitch(machine.getAccountId())) {
            log.info("记录头盔丢失日志-------------------------------------secondType：{},rideLog：{}", secondType, JSON.toJSONString(rideLog));
            // 记录头盔丢失日志
            createOrderHelmetLostLog(machine, user.getUserId(), rideLog.getOrderNO());
        }
        /**查询用户是否够金额还车*/
        /**骑行费用**/
//        Double rideMoney = 0D;
        /**停车费用**/
        Double stopMoney = 0D;
        /**礼品卡*/
        GiftCardUser giftCardUser = giftCardUserService.getByUserId(user.getUserId(), machine.getAccountId());
        /**礼品卡余额*/
        Integer giftCardMoney = Optional.ofNullable(giftCardUser).map(o -> o.getMoney()).orElseGet(() -> 0);
        /**是否使用时间优惠券*/
        CouponUserDto couponUserDB = new CouponUserDto();
        /**查询用户购买的会员卡*/
        VipLogDot vipLogDot = vipCardLogService.getUserVip(machine.getAccountId(), user.getUserId(), feeId);
        /**会员卡的自定义计费规则*/
        AdAccountFee vipFee = Optional.ofNullable(vipLogDot)
                .filter(o -> o.getVipType().equals(MemberCardConstant.VIP_CARD_VIPFEE))
                .map(o -> adAccountFeeService.getVipFee(rideLog.getAccountId(), rideLog.getStartTime(), o.getVipId())).orElseGet(() -> null);
        /**兼容vip卡自定义计费规则*/
//		AdAccountFee fee = null!=vipFee ? vipFee : adAccountFee;
        /**会员卡减免费用*/
        Double vipDiscount = 0D;
        /**区域参数配置**/
        AccountConfigExt accountConfigExt = cacheService.getAccountConfigExtByKey(machine.getAccountId(),
                AccountConfigExtConstant.ACCOUNTCONFIG_RIDEVARD);
        /**骑行卡超时才会触发*/
        String param = Optional.ofNullable(accountConfigExt).map(o -> o.getParamValue()).orElseGet(() -> "");
        /**是否有临时停车记录未结束*/
        String endTime = DateTimeUtil.getNowTime();

        Boolean isFreeRide = false;
        /**骑行里程*/
        Integer mileage = 0;
        try {
            mileage = Optional.ofNullable(historyService.getMileageByMachineNOAndTime(machine.getMachineNO(),
                    rideLog.getStartTime(), endTime)).orElseGet(() -> 0);
        } catch (Exception e) {
            log.error("获取里程异常", e);
        }
        Integer userRideCount = rideLogService.getTodayNo(user.getUserId(), DateTimeUtil.getNowTimeYMD());
        /**费用清单**/
        HashMap<String, Double> dataMap = new HashMap<String, Double>();
        /**米转公里*/
        double mileageDB = (double) mileage / 1000;
        BigDecimal bigDecimal = new BigDecimal(mileageDB);
        bigDecimal.setScale(3, RoundingMode.HALF_UP);
        mileageDB = bigDecimal.doubleValue();
        log.info("骑行订单{},获取的路程为{},计算的公里数为{}", rideLog.getOrderNO(), mileage, mileageDB);
        /**订单起始时间**/
        String rideStartTime = rideLog.getStartTime();
        /**订单结束时间**/
        String rideEndTime = Optional.ofNullable(rideLog.getEndTime()).orElseGet(() -> endTime);
        /**添加结束时间*/
        rideLog.setEndTime(rideEndTime);
        /**骑行时长(分钟)*/
        Integer rideTime = DateTimeUtil.getMinuteByTime(rideStartTime, rideEndTime, 1, 0);
        /**0.骑行费用(无任何优惠，不包含罚款)		订单里程，订单时长，免费时长，计费规则，**/
        Double cyclingMoney = MoneyUtil.getRide(mileageDB, rideTime, adAccountFee, false);
        /**人工结束订单费用自定义*/
        if (managerMoney != null && returnType == 2) {
            cyclingMoney = managerMoney.doubleValue();
        }
        dataMap.put(DiscountTypeEnum.CYCLINGMONEY.getCode(), cyclingMoney);
        /**是否可以使用新用户首单免费：新用户优惠每次免费骑行时长>0 && 查询总数<免费骑行次数**/
        Boolean isNewUserFree = newUserFreeTime > 0 && rideLogService.queryCount(user.getUserId()) < newUserFreeTimes;

        /**其他抵扣金额:新用户免费骑行、新用户调度费减免、特殊人员折扣抵扣的骑行费用的金额**/
        Double otherDeductionMoney = 0d;
        /**==================================================特殊认证==================================================**/
        AccountDiscount accountDiscount = specialAuthService.getDiscount(machine.getAccountId(), user);

        /**==================================================骑行卡==================================================**/
        /**骑行卡购买记录**/
        List<RideCardUser> rideCardUsers = rideCardUserService.getRideVaildCard(user, machine.getAccountId(), feeId);
        //校验骑行卡是否在有效日期和有效周几
        rideCardUsers = rideCardService.checkValidaDate(rideCardUsers, rideStartTime);
        RideCardUser rideCardUser = Optional.ofNullable(rideCardUsers).filter(o -> o.size() > 0).map(o -> o.get(0)).orElseGet(() -> null);
        /**第一张骑行卡**/
        RideCard rideCard = Optional.ofNullable(rideCardUser).map(o -> rideCardService.getById(o.getRideCardId())).orElseGet(() -> null);
        /**==================================================优惠券==================================================**/
        /**按时间排序获取时间优惠券*/
        List<CouponUserDto> dtoList = couponUserService.getByCouponType(machine.getAccountId(), user.getUserId());

        /**查询用户是否有生效中的骑行卡*/
        String rideCardOrderNO = null;
        /**骑行卡减免费用金额*/
        Double rideCardMoney = null;
        /**特殊认证减免金额*/
        Double isSpecial = 0D;
        /**新用户免费(时长超时)**/
        Boolean isNewUserOvertime = false;
        /**新用户免费骑行(未超时)**/
        Boolean isNewUserIntime = false;
        /**订单金额，缓存**/
        Double cyclingMoneyCache = cyclingMoney;
        /**领导账号**/
        /**============================================特殊逻辑判断============================================**/
        /**是否领导账号**/
        boolean isleader = false;
        /**兼容领导账号逻辑,不扣任何费用*/
        LeaderAccount leaderAccount = leaderAccountDao.getByPhone(userphone, user.getAccountId());
        /**领导账号 && 品牌id相同 && 账号未到期**/
        if (leaderAccount != null && leaderAccount.getAccountId().equals(user.getAccountId()) && System.currentTimeMillis() < leaderAccount.getExpireTime().getTime()) {
            cyclingMoneyCache = 0D;
            money = 0D;
            dispatchMoney = null;
            isleader = true;
            //			list.add(new Discount("领导账号",0,cyclingMoneyCache));
        }
        /***/
        Boolean isMaas = false;
        if (user.getDepositState() == DepositStateConstant.DEPOSIT_STATE_VIRTUAL) {
            isMaas = true;
        }

        /**============================================同类优惠互斥锁============================================**/
        /**是否使用了骑行卡**/
        Boolean userRideCard = false;
        /**是否使用了费用骑行卡**/
        Boolean rideCardFee = false;
        /**是否使用了优惠券**/
        Boolean userCoupon = false;
        /**是否使用了vip**/
        Boolean userVip = false;
        /**是否使用了vip自定义计费**/
        Boolean userVipfee = false;
        /**是否使用了礼品卡**/
        Boolean userGift = false;
        /**优惠券金额*/
        Integer couponMoney = 0;
        /**是否骑行卡超时*/
        Boolean isRideOvertime = false;
        /**骑行卡免费抵扣费用*/
        Double rideCardOverFEE = 0D;
        /**金额优惠券*/
        CouponUserDto moneyCouponUser = new CouponUserDto();
        /**============================================1.每日免费骑行（时长减免）============================================**/
        /**免费信息*/
        String freeInfo = "";
        if (cyclingMoneyCache > 0 && isArtificial && accountFreeTime > userRideCount) {
            int freeType = MoneyUtil.countFreeType(rideTime, mileage, freeTime, freeDistance, freeConditionType);
            if (freeType > 0) {
                cyclingMoneyCache = 0d;
                isFreeRide = true;

                if (freeType == 1) {
                    dataMap.put(DiscountTypeEnum.DAYTIME.getCode(), cyclingMoneyCache);
                    freeInfo = "前" + freeTime + "分钟内订单免费骑行";
                }
                if (freeType == 2) {
                    dataMap.put(DiscountTypeEnum.DAYDISTANCE.getCode(), cyclingMoneyCache);
                    freeInfo = "前" + freeDistance + "米内订单免费骑行";
                }
                if (freeType == 3) {
                    dataMap.put(DiscountTypeEnum.DAYTIME_AND_DAYDISTANCE.getCode(), cyclingMoneyCache);
                    freeInfo = "前" + freeTime + "分钟内，享受" + freeDistance + "米内订单免费骑行";
                }
                if (freeType == 4) {
                    dataMap.put(DiscountTypeEnum.DAYTIME_OR_DAYDISTANCE.getCode(), cyclingMoneyCache);
                    freeInfo = "前" + freeTime + "分钟或" + freeDistance + "米内订单免费骑行";
                }
                // 版本5.0 短时骑行的详情增加 禁停区调度费信息
                if (Objects.nonNull(disPatchData) && Objects.nonNull(disPatchData.getNoParkAreaMoney())) {
                    freeInfo = freeInfo + ",禁停区调度费:" + DoubleUtil.format(disPatchData.getNoParkAreaMoney() / 100) + "元";
                }
            }
        }
        /**============================================2.新用户优惠（时长减免）============================================**/
        if (cyclingMoneyCache > 0 && isNewUserFree && isArtificial) {
            if (rideTime <= newUserFreeTime) {
//                list.add(new Discount("2", rideTime, cyclingMoneyCache));
                dataMap.put(DiscountTypeEnum.NEWUSERTIME.getCode(), cyclingMoneyCache);
                cyclingMoneyCache = 0d;
                isNewUserIntime = true;
                //freeTimeDiscount = freeTimeDiscount + rideTime;
            } else {
                rideTime = rideTime - newUserFreeTime;
                Double moneyCache = MoneyUtil.getRide(mileageDB, rideTime, adAccountFee, false);
//                list.add(new Discount("2", newUserFreeTime, cyclingMoneyCache - moneyCache));
                dataMap.put(DiscountTypeEnum.NEWUSERTIME.getCode(), cyclingMoneyCache - moneyCache);
                cyclingMoneyCache = moneyCache;
                isNewUserOvertime = true;
                //freeTimeDiscount = freeTimeDiscount + newUserFreeTime;
            }
        }
        /**============================================3.骑行卡（时长减免）============================================**/
        if (cyclingMoneyCache > 0 && !userRideCard && null != rideCard
                && MemberCardConstant.RIDE_CARD_TIME.equals(rideCard.getRidecardType()) && isArtificial) {
            /**骑行卡减免时长**/
            Integer cardTime = Optional.ofNullable(rideCard.getTimeCount()).orElseGet(() -> 0);
            /**骑行卡时长减免=0 说明无时间限制**/
            if (cardTime == 0 || rideTime <= cardTime) {
//                list.add(new Discount("3", rideTime, cyclingMoneyCache));
                dataMap.put(DiscountTypeEnum.CARDTIME.getCode(), cyclingMoneyCache);
                rideCardOverFEE = cyclingMoneyCache;
                cyclingMoneyCache = 0d;
            } else {
                rideTime = rideTime - cardTime;
                Boolean isRideFee = false;
                /**骑行卡超时才会触发*/
                if (accountConfigExt != null && "1".equals(accountConfigExt.getParamValue())) {
                    isRideFee = true;
                }
                Double moneyCache = MoneyUtil.getRide(mileageDB, rideTime, adAccountFee, isRideFee);
//                list.add(new Discount("3", cardTime, cyclingMoneyCache - moneyCache));
                dataMap.put(DiscountTypeEnum.CARDTIME.getCode(), cyclingMoneyCache - moneyCache);
                rideCardOverFEE = cyclingMoneyCache - moneyCache;
                cyclingMoneyCache = moneyCache;
                isRideOvertime = true;
            }
            rideCardOrderNO = rideCardUser.getOrderNO();
            userRideCard = true;
        }
        /**============================================4.优惠券（时长减免）============================================**/
        if (cyclingMoneyCache > 0 && !userCoupon && isArtificial) {
            /**获取时长最近接的一张优惠券**/
            CouponUserDto couponUser = MoneyUtil.getTimeCoupon(dtoList, rideTime);
            if (null != couponUser) {
                /**优惠券减免时长**/
                Integer timeCount = couponUser.getCouponTime();
                if (rideTime <= timeCount) {
//                    list.add(new Discount("4", rideTime, cyclingMoneyCache));
                    dataMap.put(DiscountTypeEnum.CONPONTIME.getCode(), cyclingMoneyCache);
                    cyclingMoneyCache = 0d;
                } else {
                    rideTime = rideTime - timeCount;
                    Double moneyCache = MoneyUtil.getRide(mileageDB, rideTime, adAccountFee, false);
//                    list.add(new Discount("4", timeCount, cyclingMoneyCache - moneyCache));
                    dataMap.put(DiscountTypeEnum.CONPONTIME.getCode(), cyclingMoneyCache - moneyCache);
                    cyclingMoneyCache = moneyCache;
                }
                userCoupon = true;
                couponUserDB = couponUser;
            }
        }
        /**============================================5.会员自定义计费规则============================================**/
        if (cyclingMoneyCache > 0 && !userVip && null != vipFee && isArtificial) {
            Integer vipFreeTime = vipFee.getFreeTime();
            Integer vipfreeConditionType = vipFee.getFreeConditionType();
            Integer vipfreeDistance = vipFee.getFreeDistance();
            /**判断是否符合会员免费骑行*/
            int freeType = MoneyUtil.countFreeType(rideTime, mileage, vipFreeTime, vipfreeDistance, vipfreeConditionType);
            if (freeType > 0) {
//                list.add(new Discount("5", rideTime, cyclingMoneyCache));
                dataMap.put(DiscountTypeEnum.VIPRULE.getCode(), cyclingMoneyCache);
                vipDiscount = cyclingMoneyCache;
                cyclingMoneyCache = 0d;
            } else {
                Double moneyCache = MoneyUtil.getRide(mileageDB, rideTime, vipFee, false);
//                list.add(new Discount("5", 0, cyclingMoneyCache - moneyCache));
                dataMap.put(DiscountTypeEnum.VIPRULE.getCode(), cyclingMoneyCache - moneyCache);
                vipDiscount = cyclingMoneyCache - moneyCache;
                cyclingMoneyCache = moneyCache;
            }
            userVip = true;
            userVipfee = true;
        }
        /**============================================6.骑行卡（费用减免）============================================**/
        if (cyclingMoneyCache > 0 && !userRideCard && null != rideCard
                && MemberCardConstant.RIDE_CARD_REDUCE.equals(rideCard.getRidecardType()) && isArtificial) {
            Integer cardAmount = Optional.ofNullable(rideCard.getTimeCount()).orElseGet(() -> 0);
            /**减免金额：订单金额<=优惠金额 ? 订单金额:优惠金额 **/
            double cache = cyclingMoneyCache <= cardAmount ? cyclingMoneyCache : cardAmount.doubleValue();
            /**减免后金额**/
            cyclingMoneyCache = cyclingMoneyCache - cache;
//            list.add(new Discount("6", 0, cache));
            dataMap.put(DiscountTypeEnum.CARDFEE.getCode(), cache);
            rideCardMoney = cache;
            rideCardOrderNO = rideCardUser.getOrderNO();
            userRideCard = true;
            rideCardFee = true;
        }
        /**============================================7.会员卡折扣（费用减免）============================================**/
        if (cyclingMoneyCache > 0 && !userVip && null != vipLogDot && isArtificial) {
            Integer vipType = vipLogDot.getVipType();
            Double discount = vipLogDot.getDiscount();
            if (vipType.equals(MemberCardConstant.VIP_CARD_DISCOUNT)) {
                /**会员卡（折扣）**/
                Double vipMoney = MoneyUtil.formatDouble(cyclingMoneyCache * discount / 10);
//                list.add(new Discount("7", 0, cyclingMoneyCache - vipMoney));
                dataMap.put(DiscountTypeEnum.VIPFEE.getCode(), cyclingMoneyCache - vipMoney);
                vipDiscount = cyclingMoneyCache - vipMoney;
                cyclingMoneyCache = vipMoney;
            } else if (vipType.equals(MemberCardConstant.VIP_CARD_REDUCE)) {
                /**会员卡（费用减免）**/
                double vipdiscount = discount * 100;
                /**减免金额**/
                double cache = cyclingMoneyCache <= vipdiscount ? cyclingMoneyCache : vipdiscount;
                /**减免后金额**/
                cyclingMoneyCache = cyclingMoneyCache - cache;
//                list.add(new Discount("7", 0, cache));
                dataMap.put(DiscountTypeEnum.VIPFEE.getCode(), cache);
                vipDiscount = cache;

            }
            userVip = true;
        }
        /**============================================8.特殊认证折扣（费用减免）============================================**/
        if (cyclingMoneyCache > 0 && accountDiscount != null && isArtificial) {
            Double spMoney = MoneyUtil.formatDouble((cyclingMoneyCache * accountDiscount.getDiscount().doubleValue()));
            isSpecial = cyclingMoneyCache - spMoney;
//            list.add(new Discount("8", 0, cyclingMoneyCache - spMoney));
            dataMap.put(DiscountTypeEnum.SPECIALFEE.getCode(), cyclingMoneyCache - spMoney);
            cyclingMoneyCache = spMoney;
        }
        /**============================================9.优惠券（费用减免）============================================**/
        if (cyclingMoneyCache > 0 && !userCoupon && isArtificial) {
            /**获取金额最大的优惠券**/
            CouponUserDto couponUser = couponUserService.getById(machine.getAccountId(), user.getUserId());
            if (null != couponUser && couponUser.getCouponType() == 0) {
                couponMoney = Optional.ofNullable(couponUser.getCouponMoney()).orElseGet(() -> 0);
                /**减免金额**/
                double cache = cyclingMoneyCache <= couponMoney ? cyclingMoneyCache : couponMoney.doubleValue();
                /**减免后金额**/
                cyclingMoneyCache = cyclingMoneyCache - cache;
//                list.add(new Discount("优惠券（费用减免）",0,cache));
                dataMap.put(DiscountTypeEnum.CONPONFEE.getCode(), cache);
                moneyCouponUser = couponUser;
                map.put("couponMoney", String.valueOf((int) cache));
            }
        }
        /**============================================10礼品卡（费用减免）============================================**/
        if (cyclingMoneyCache > 0 && giftCardMoney > 0d && isArtificial) {
            /**减免金额**/
            double cache = cyclingMoneyCache <= giftCardMoney ? cyclingMoneyCache : giftCardMoney.doubleValue();
            /**减免后金额**/
            //cyclingMoneyCache = cyclingMoneyCache-cache;
            dataMap.put(DiscountTypeEnum.GIFTCARDMONEY.getCode(), cache);
            map.put("giftCardMoney", String.valueOf((int) cache));
            userGift = true;
        }
        /**===========================================计费结束======================================================**/

        dataMap.put(DiscountTypeEnum.DEDUCTIONCYCLINGMONEY.getCode(), cyclingMoneyCache);
        /**用户待付总金额**/
        Double allMoney = cyclingMoneyCache;
        /**添加业务信息*/
        BorrowReturnTer info = new BorrowReturnTer();
        /**豁免金额*/
        String reduce = redisService.get(RedisConstant.RIDELOG_REDUCE + rideLog.getOrderNO());
        if (reduce != null) {
            /**调度类型：支持调度不在运营区域 = 超区豁免次数；调度类型：其他 = 站外豁免次数	**/
            Integer type = null;
            if (DisPatchConstant.DISPATCH_NOT_IN_AREA.equals(dispatchType)) {
                type = UserDiscountStatConstant.USERDISCOUNTSTAT_OUT_OF_AREA;
            } else if (DisPatchConstant.DISPATCH_NO_IN_PARK.equals(dispatchType)) {
                type = UserDiscountStatConstant.USERDISCOUNTSTAT_OUT_OF_PARK;
            } else {
                type = UserDiscountStatConstant.USERDISCOUNTSTAT_OUT_OF_NOPARKAREA;
            }
            info.setDiscountType(type);
        }
        Double reduceMoney = Optional.ofNullable(reduce).map(o -> Double.valueOf(o)).orElseGet(() -> 0d);
        /**===========================================11.调度费豁免======================================================**/
        /**===========================================12.调度费豁免======================================================**/
        if (null != dispatchMoney && dispatchMoney > 0 && !isMaas) {
            dataMap.put(DiscountTypeEnum.DISPATCHMONEY.getCode(), (double) dispatchMoney);
            info.setPhone(userphone);
            info.setDispatchMoney(dispatchMoney);
            if (reduceMoney > 0 && (DisPatchConstant.DISPATCH_NOT_IN_AREA.equals(dispatchType) || DisPatchConstant.DISPATCH_NO_IN_PARK.equals(dispatchType) || DisPatchConstant.DISPATCH_NO_PARK_AREA.equals(dispatchType))) {
//                list.add(new Discount("11", 0, dispatchMoney));
                dataMap.put(DiscountTypeEnum.REDUCEMONEY.getCode(), reduceMoney);

                dispatchMoney = NumberUtil.sub(dispatchMoney, reduceMoney);
                info.setDiscountMoney(reduceMoney.intValue());
            }
            /**================================用户待付总金额 = 订单金额+调度费用================================**/
            allMoney = allMoney + dispatchMoney;
        }
        MachineStatus machineStatus = machineStatusService.getByMachineNO(machine.getMachineNO(), MapConstant.MAP_BAIDU);
        if (machineStatus != null && machineStatus.getLon() != null && machineStatus.getLat() != null) {
            info.setLo(machineStatus.getLon());
            info.setLa(machineStatus.getLat());
        } else {
            info.setLo(0.0);
            info.setLa(0.0);
        }
        /**allMoney不减免礼品卡金额**/
        /**计算不足的金额，返回*/
        if ((user.getMoney() + giftCardMoney) < allMoney) {

            money = allMoney - user.getMoney() - giftCardMoney;
            FeeDeviation feeDeviationNew = new FeeDeviation();
            feeDeviationNew.setNeedPayMoney(money);
            feeDeviationNew.setEndTime(endTime);
            feeDeviationNew.setUserId(user.getUserId());
            feeDeviationNew.setOperaTime(System.currentTimeMillis());
            feeDeviationNew.setMoney(allMoney);
            /**骑行卡锁定*/
            feeDeviationNew.setRideCardOrderNO(rideCardOrderNO);
            feeDeviationNew.setRideOvertime(isRideOvertime);
            /**骑行订单备注也存放内存*/
            feeDeviationNew.setRemark(info.getRemark());
            /**调度费用*/
            if (dispatchMoney != null) {
                feeDeviationNew.setDispatchMoney(dispatchMoney);
            }
            /**添加数据*/
            redisService.add(RedisConstant.DEVIATION_KEY_PREFIX + user.getUserId(), JSON.toJSONString(feeDeviationNew), 60);
        }

        HashMap<String, Long> detailsMap = MoneyUtil.calculationFeeDetail(dataMap);

        /**停车时长（min）**/
        Integer stopTime = Optional.ofNullable(park).map(o -> DateTimeUtil.getTime(o.getStartTime(), endTime)).orElseGet(() -> 0);
        /**记录停车时长*/
        if (stopTime > 0) {
            info.setStopTime(stopTime);
            rideLog.setStopTime(stopTime);
        }

        if (rideLog.getStopTime() != null) {
            /**中途停车时长(不过1分钟按一分钟计算)*/
            stopTime = rideLog.getStopTime() / 60 + (rideLog.getStopTime() % 60 > 0 ? 1 : 0);
        }

        /**不存在临时停车记录，则下发指令*/
        info.setOrderNO(rideLog.getOrderNO());
        info.setType(0);
        info.setRideOvertime(isRideOvertime);
        info.setAccountId(machine.getAccountId());
        info.setMachineId(machine.getMachineId());
        info.setUserId(user.getUserId());
        info.setMachineNO(machine.getMachineNO());
        info.setUserCode(machine.getUserCode());
        /**强制格式化保留两位小数*/
        allMoney = Double.parseDouble(String.format("%.2f", allMoney));
        info.setMoney(allMoney);
        info.setMileage(mileageDB);
        info.setParkPointId(parkPointId);
        info.setDelType(0);
        info.setCouponUserDto(couponUserDB);
        info.setEndTime(endTime);
        info.setCouponUser(moneyCouponUser);
        info.setPayMoney(money);
        Double totalMoney = cyclingMoney + (dispatchMoney == null ? 0 : dispatchMoney);
        info.setDisPatchData(disPatchData);

        String remark = "总费用:" + Double.parseDouble(String.format("%.2f", totalMoney / 100)) + "元";
        /**未使用骑行卡 || 骑行卡超时 || 骑行卡减免费用金额不为空**/

        if ((capTimeFee != 0 && ((double) capTimeFee == allMoney))) {
            /**计时封顶金额不为0 && 计费封顶金额等于待付金额**/

            remark = remark + ",最高骑行时间费用限制:" + capTimeFee * 1.0 / 100 + "元";
        } else if ((capMileFee != 0 && ((double) capMileFee == allMoney))) {
            /**里程封顶金额不为0 && 里程封顶金额等于待付金额**/

            remark = remark + ",最高骑行里程费用限制:" + capMileFee * 1.0 / 100 + "元";
        } else {

            remark = "总费用:" + totalMoney / 100 + "元,骑行费用:" + DoubleUtil
                    .format(cyclingMoney / 100) + "元,停车费用:" + DoubleUtil.format(stopMoney / 100) + "元,停车时长:" + stopTime + "分钟";
            if (Objects.nonNull(disPatchData) && Objects.nonNull(disPatchData.getNoParkAreaMoney())) {
                remark = remark + ",禁停区调度费:" + DoubleUtil.format(disPatchData.getNoParkAreaMoney() / 100) + "元";
            }
        }

        if (isFreeRide) {
            remark = freeInfo;
        }

        /**首单用户免费 骑行超时*/
        if (isNewUserOvertime) {
            info.setDelType(RideLogDetailConstant.DETAILLOG_DISCOUNT);
            remark = remark + ",新用户免费骑行超时";
        }
        if (isNewUserIntime) {
            remark = "新用户免费骑行";
        }
        /**是否是骑行卡骑行超时*/
        if (isRideOvertime) {
            info.setRideCardOrderNO(rideCardOrderNO);

            remark = remark + ",时长骑行卡抵扣费用:" + rideCardOverFEE / 100 + "元";
        }
        /**是否使用会员卡*/
        if (userVipfee) {
            remark = remark + ",会员卡单独计费";
        }
        if (vipDiscount > 0) {

            remark = remark + ",会员卡费用减免:" + vipDiscount / 100 + "元";
        }
        /**有使用骑行费用减免卡*/
        if (rideCardFee) {
            info.setRideCardOrderNO(rideCardOrderNO);
            info.setRidecardMoney(rideCardMoney);

            remark = remark + ",骑行卡费用减免:" + rideCardMoney / 100 + "元";
        }
        if (isSpecial > 0) {

            remark = remark + ",特殊认证用户减免:" + isSpecial / 100 + "元";
        }
        if (null != rideCardOrderNO && !isRideOvertime && !rideCardFee) {
            info.setRideCardOrderNO(rideCardOrderNO);
            info.setMoney(0D);
            info.setDelType(RideLogDetailConstant.DETAILLOG_RIDECARD);
            remark = "骑行卡免费骑行";
        }
        if (isleader) {
            info.setCouponUserDto(new CouponUserDto());
            info.setRidecardMoney(null);
            info.setRideCardOrderNO(null);
            info.setRideOvertime(false);
            remark = "VIP专享，免费骑行！";
        }
        /**兼容第三方骑行,虚拟押金用户为第三方用户，不扣费用，全部置为其他抵扣费用*/
        if (isMaas) {
            otherDeductionMoney = allMoney;
            allMoney = 0D;
            money = 0D;
            info.setDispatchMoney(null);
            info.setCouponUserDto(new CouponUserDto());
            info.setRidecardMoney(null);
            info.setRideCardOrderNO(null);
            info.setRideOvertime(false);
            info.setMoney(0D);
            remark = remark + "第三方账号免费骑行";
            detailsMap.put("otherDeductionMoney", otherDeductionMoney.longValue());
        }

        /**--------------------融合定位包逻辑开始--------------------**/
        AccountConfigExt fuselocationFree = cacheService.getAccountConfigExtByKey(machine.getAccountId(),
                AccountConfigExtConstant.FUSE_LOCATION_FREE);
        Integer threshold = Optional.ofNullable(fuselocationFree).map(o -> Integer.valueOf(o.getParamValue())).orElseGet(() -> null);
//		Boolean isfuselocationFree = null!=fuselocationFree && Objects.equals(fuselocationFree.getParamValue(), "1") ? true :false;
        /**车辆gps里程，3分钟内结束订单不收费。（不收调度费，不收骑行费用  对于 头盔需要判断）**/
        int time = DateTimeUtil.getTime(rideLog.getStartTime(), DateTimeUtil.getNowTime());
        log.info("订单号[" + rideLog.getOrderNO() + "]里程[" + mileage + "米]订单时长[" + time + "秒]距离阈值[" + threshold + "]");
        //非人工结束才能走融合定位包逻辑
        if (null != threshold && time <= 3 * 60l && mileage <= threshold && returnType != 2) {

            //头盔调度费用
            Double helmetMoney = 0d;
            if (secondType != null && secondType.equals(DisPatchConstant.DISPATCH_HELMET)) {
                //开启头盔调度开关
                if (adAccountFee != null && adAccountFee.getHelmetSwitch() == 1) {
                    helmetMoney = (double) adAccountFee.getHelmetMoney();
                }
            }
            allMoney = helmetMoney;
            money = helmetMoney;
            dispatchMoney = helmetMoney;
            info.setDispatchMoney(dispatchMoney);
            info.setCouponUserDto(new CouponUserDto());
            info.setRidecardMoney(null);
            info.setRideCardOrderNO(null);
            info.setRideOvertime(false);

            info.setMoney(money);
            //info.setRemark("里程为0，3分钟内结束订单不收费");

            remark = remark + ",里程为0，3分钟内结束订单不收费";
        }
        /**--------------------融合定位包逻辑结束--------------------**/
        info.setRemark(remark);
        Integer brandId = user.getAccountId();
        RideLogDetails rideLogDetails = new RideLogDetails();
        rideLogDetails.setOrderId(rideLog.getOrderNO());//骑行订单号
        rideLogDetails.setAccountId(machine.getAccountId());//运营区域id
        rideLogDetails.setBrandId(brandId);//品牌id
        rideLogDetails.setAccountName(account.getName());//运营地区名称
        rideLogDetails.setOrderType(RideLogDetailsConstant.ORDER_TYPE_NORMAL);//订单类型 1：正常结束 2：欠款补交 3：欠款
        if (money > 0) {
            rideLogDetails.setPayStatus(RideLogDetailsConstant.ORDER_ARREARS);//支付状态1：已支付，2：未支付
        } else {
            rideLogDetails.setPayStatus(RideLogDetailsConstant.ORDER_PAY);//支付状态1：已支付，2：未支付
            info.setPayTime(DateTimeUtil.getNowTime());
        }
        rideLogDetails.setUserId(user.getUserId());//用户id
        rideLogDetails.setUserPhone(userphone);//手机号
        rideLogDetails.setUserName(user.getName());//用户名
        rideLogDetails.setMachineId(machine.getMachineId());
        rideLogDetails.setUserCode(machine.getUserCode());//车辆编号
        rideLogDetails.setMachineNO(machine.getMachineNO());//设备编号
        rideLogDetails.setOrderStartTime(DateTimeUtil.StringToDateTime(rideStartTime));//订单开始时间
        rideLogDetails.setOrderEndTime(DateTimeUtil.StringToDateTime(rideEndTime));//订单结束时间
        rideLogDetails.setMileage(mileage);//里程
        rideLogDetails.setCyclingMoney(cyclingMoney.longValue());//骑行费用
        if (info.getDispatchMoney() != null) {
            rideLogDetails.setFineMoney(info.getDispatchMoney().longValue());//罚款费用
        }
        rideLogDetails.setValid(0);
        if (isFreeRide) {
            rideLogDetails.setValid(1);
        }
        //dispatchType 3不在运营区域 4不在还车点

        rideLogDetails.setFineType(MoneyUtil.mapDispatchType(dispatchType, secondType));//罚款类型1：站外停车，2：运营区域外停车，3：头盔锁未归还
        rideLogDetails.setCardDeductionMoney(detailsMap.get("cardDeductionMoney"));//卡券抵扣金额
        rideLogDetails.setGiftCardDeductionMoney(detailsMap.get("giftCardDeductioMoney"));//礼品卡抵扣费用
        rideLogDetails.setOtherDeductionMoney(detailsMap.get("otherDeductionMoney"));//其他抵扣费用
        rideLogDetails.setDeduciontMoney(detailsMap.get("deduciontMoney"));//总抵扣费用
        if (returnType == 2) {
            rideLogDetails.setDeductionEndMoney(allMoney.longValue());//抵扣后总费用
        } else {
            rideLogDetails.setDeductionEndMoney(detailsMap.get("deductionEndMoney"));//抵扣后总费用
        }
        rideLogDetails.setOperatorId(operatorId);//操作人ID
        rideLogDetails.setOperatorName(operatorName);//操作人名称
        rideLogDetails.setOperatorPhone(operatorPhone);
        rideLogDetails.setBillingDetails(remark);//计费详情
        rideLogDetails.setVipCard(detailsMap.get("vipCard"));//会员卡抵扣费用
        rideLogDetails.setCouponCard(detailsMap.get("couponCard"));//优惠券抵扣费用
        rideLogDetails.setRideCard(detailsMap.get("rideCard"));//骑行卡抵扣费用
        if (afterPay.equals(AccountConfigExtConstant.ACCOUNTCONFIG_AFTERPAY_OPEN)) {
            info.setLessMoney(money.intValue());
        }
        /**用户还车信息缓存*/

        redisService.add(RedisConstant.USER_FINISH_PAY + user.getUserId(), serNO, 60);
        redisService.add(serNO, JSONObject.toJSONString(info), 60);

        redisService.add(RedisConstant.RIDELOG_DETAILS + user.getUserId(), JSONObject.toJSONString(rideLogDetails), 300);

        if (dispatchMoney != null) {
            map.put("dispatchMoney", String.valueOf(dispatchMoney));
        }
        map.put("allMoney", String.valueOf(allMoney));
        map.put("freeTime", String.valueOf(freeTime));
        map.put("endTime", endTime);
        return money;
    }

    /**
     * 计算用户骑行分
     *
     * @param userId    用户id
     * @param fineType  罚款类型 1：站外停车，2：运营区域外停车，3：头盔锁未归还
     * @param accountId 区域id
     * @param brandId   品牌id
     */
    private void computeUserRidingScore(Integer userId, String fineType, Integer accountId, Integer brandId) {
        UserRidingScoreConfig userRidingScoreConfig = cacheService.getUserRidingScoreConfig(brandId);
        if (fineType == null) {
            ridingScoreDetails(userId, UserRidingScoreConstant.STANDARD_PARKING, userRidingScoreConfig);
        } else {
            String[] fType = fineType.split(",");
            for (String s : fType) {
                //减分项

                switch (Integer.parseInt(s)) {
                    case UserRidingScoreConstant.OUT_SIDE:
                        //站外判断是站点模式还是禁停区模式
                        Integer type = UserRidingScoreConstant.OVERFLOW_RETURN;
                        AccountConfig accountConfig = accountConfigService.getByAccountId(accountId);
                        //还车模式：0站点1禁停区
                        Integer returnType = accountConfig == null ? 0 : accountConfig.getReturnModel() == null ? 0 : accountConfig.getReturnModel();
                        if (returnType == 1) {
                            type = UserRidingScoreConstant.PROHIBIT_AREA_RETURN;
                        }
                        ridingScoreDetails(userId, type, userRidingScoreConfig);
                        break;
                    case UserRidingScoreConstant.OUT_AREA:
                        ridingScoreDetails(userId, UserRidingScoreConstant.OVERFLOW_AREA_RETURN, userRidingScoreConfig);
                        break;
                    case UserRidingScoreConstant.HELMET_NOT_RETURNED:
                        //头盔锁未还暂时无减分
                        break;
                    default:
                        break;
                }
            }


        }
    }

    /**
     * 加减分详情
     *
     * @param userId                用户id
     * @param type                  加减分类型
     * @param userRidingScoreConfig 加减分配置
     */
    private void ridingScoreDetails(Integer userId, int type, UserRidingScoreConfig userRidingScoreConfig) {
        int score = 0;
        switch (type) {
            case UserRidingScoreConstant.STANDARD_PARKING:
                //无罚款(加分项)
                score = userRidingScoreConfig.getStandardParking();
                updateRidingScore(userId, score, true, UserRidingScoreConstant.STANDARD_PARKING);
                break;
            case UserRidingScoreConstant.OVERFLOW_RETURN:
                score = userRidingScoreConfig.getOverflowReturn();
                updateRidingScore(userId, score, false, UserRidingScoreConstant.OVERFLOW_RETURN);
                break;
            case UserRidingScoreConstant.PROHIBIT_AREA_RETURN:
                score = userRidingScoreConfig.getProhibitAreaReturn();
                updateRidingScore(userId, score, false, UserRidingScoreConstant.PROHIBIT_AREA_RETURN);
                break;
            case UserRidingScoreConstant.OVERFLOW_AREA_RETURN:
                score = userRidingScoreConfig.getOverflowAreaReturn();
                updateRidingScore(userId, score, false, UserRidingScoreConstant.OVERFLOW_AREA_RETURN);
                break;
            default:
                break;
        }
    }


    /**
     * 加减分
     *
     * @param userId 用户id
     * @param score  配置的分数
     * @param isAdd  是否是加分项
     */
    void updateRidingScore(Integer userId, int score, boolean isAdd, int type) {
        //防止配置有误
        if (score > 0 && isAdd) {
            //配置的分数是正数，而且是加分项
            Integer oldUserRidingScore = userService.getUserRidingScore(userId);
            if (oldUserRidingScore >= UserRidingScoreConstant.MAX_SCORE) {
                //分值已达到最大，不进行加分
                return;
            }
            Integer newUserRidingScore = score + oldUserRidingScore;
            if (newUserRidingScore > UserRidingScoreConstant.MAX_SCORE) {
                //加的分值设置为刚好加到600
                score = UserRidingScoreConstant.MAX_SCORE - oldUserRidingScore;
                newUserRidingScore = UserRidingScoreConstant.MAX_SCORE;
            }
            userService.updateUserRidingScore(userId, newUserRidingScore);
            UserRidingScoreLog userRidingScoreLog = new UserRidingScoreLog(null, userId, type, oldUserRidingScore, score, newUserRidingScore, new Date(), null, null, null);
            userRidingScoreLogDao.insert(userRidingScoreLog);
            log.info("用户骑行分加分{}", JSON.toJSONString(userRidingScoreLog));
        }

        if (score < 0 && !isAdd) {
            //配置的分数是负数，而且是减分项
            Integer oldUserRidingScore = userService.getUserRidingScore(userId);
            if (oldUserRidingScore <= UserRidingScoreConstant.MIN_SCORE) {
                //分值已达到最小，不进行减分
                return;
            }
            Integer newUserRidingScore = score + oldUserRidingScore;
            if (newUserRidingScore < UserRidingScoreConstant.MIN_SCORE) {
                //减的分值设置为刚好减到0
                score = UserRidingScoreConstant.MIN_SCORE - oldUserRidingScore;
                newUserRidingScore = UserRidingScoreConstant.MIN_SCORE;
            }
            userService.updateUserRidingScore(userId, newUserRidingScore);
            UserRidingScoreLog userRidingScoreLog = new UserRidingScoreLog(null, userId, type, oldUserRidingScore, score, newUserRidingScore, new Date(), null, null, null);
            userRidingScoreLogDao.insert(userRidingScoreLog);
            log.info("用户骑行分减分{}", JSON.toJSONString(userRidingScoreLog));
        }

    }


    /**
     * 还车逻辑
     *
     * @param info
     */
    @Transactional
    @Override
    public void cash(BorrowReturnTer info) {
        //正常接还车
        /**查询骑行日志*/
        RideLog rideLog = rideLogService.getByOrderNO(info.getOrderNO());
        if (rideLog == null) {
            throw new BaseException("-3007", "不存在骑行记录");
        }
        MachineBorrow mb = machineBorrowService.getByMachineId(rideLog.getMachineId());
        if (mb == null) {
            log.warn("借车信息不存在==" + rideLog.getOrderNO());
            return;
        }
        /**如果当前指令回复的借车流水号和内存中不一致，不执行*/
        if (!info.getOrderNO().equals(mb.getOrderNO())) {
            log.warn("指令回复的借车流水号和内存中不一致,结束订单异常=" + rideLog.getOrderNO());
            return;
        }
        /**删除当前骑行记录*/
        machineBorrowService.del(rideLog.getMachineId());
        /**清除redis数据*/
        redisService.del(RedisConstant.USER_OPEN_HELMET + mb.getMachineId());
        User user = userService.getByUserId(info.getUserId());

        /*****/
        String details = redisService.get(RedisConstant.RIDELOG_DETAILS + user.getUserId());

        RideLogDetails rideLogDetails = JSON.parseObject(details, RideLogDetails.class);
        if (rideLogDetails == null) {
            log.warn("未找到缓存骑行订单" + rideLog.getOrderNO());
            return;
        }
        Integer gifMoney = 0;

        Double money = info.getMoney();
        double userMoney = user.getMoney();
        Double dispatchMoney = 0D;
        /**如果调度费不为空，需要扣除余额*/
        if (info.getDispatchMoney() != null && info.getDispatchMoney() != 0) {
            dispatchMoney = info.getDispatchMoney();
        }

        CouponUserDto couponUser = info.getCouponUserDto();
        /**有可使用的优惠券*/
        if (couponUser != null && couponUser.getCouponuserId() != null) {
            /**暂时认为停车时长无新计费规则*/
            Integer time = DateTimeUtil.getMinuteInput(rideLog.getStartTime(), 1, 0);
            couponUserService.edit(couponUser.getCouponuserId());

            String remark = ",优惠券抵消骑行时长:" + (time <= couponUser.getCouponTime() ? time : couponUser.getCouponTime()) + "分钟";
            info.setRemark(info.getRemark() + remark);
        }
        /**实际扣除本金余额*/
        Double realCashMoney = 0D;
        String payTime = DateTimeUtil.getNowTime();
        /**非骑行卡*/
        // if (info.getRideCardOrderNO() == null || info.isRideOvertime() || info.getRidecardMoney() != null) {
        /**礼品卡余额*/
        GiftCardUser giftCardUser = giftCardUserService.getByUserId(user.getUserId(), info.getAccountId());
        if (giftCardUser == null) {
            giftCardUser = new GiftCardUser();
            giftCardUser.setMoney(0);
            giftCardUser.setAccountId(info.getAccountId());
            giftCardUser.setUserId(user.getUserId());
        }
        CouponUserDto couponUserDB = info.getCouponUser();
        if (couponUserDB != null && couponUserDB.getCouponuserId() != null) {

            String remark = ",优惠券抵消:" + rideLogDetails.getCouponCard().doubleValue() / 100 + "元";
            cash(info, couponUserDB.getCouponMoney(), RechargeConstant.MONEY_TYPE_GIF,
                    RideLogDetailConstant.DETAILLOG_MONEY, remark);
            /**修改优惠券已使用*/
            couponUserService.edit(couponUserDB.getCouponuserId());
            info.setRemark(info.getRemark() + remark);
        }
        /**扣费*/
        if (money > 0 || dispatchMoney > 0) {
            BrandRemoteConfig brandConfig = cacheService.getBrandRemoteConfig(user.getAccountId(),
                    BrandConstant.BRAND_CASH_TYPE);
            Integer button = 0;
            if (brandConfig != null) {
                button = Integer.valueOf(brandConfig.getParamKV().substring(brandConfig.getParamKV().indexOf("=") + 1));
            }
            Map<String, Double> map = new HashMap<>();
            map.put("real", 0D);
            map.put("gift", 0D);
            if (dispatchMoney > 0) {

                //info.setRemark(info.getRemark() + ",管理费:" + dispatchMoney / 100 + "元");
                DisPatchData disPatchData = info.getDisPatchData();
                if (disPatchData == null) {
                    info.setRemark(info.getRemark() + ",管理费:" + dispatchMoney / 100 + "元");
                    disPatchData = new DisPatchData();
                }
                if (disPatchData.getAreaMoney() != null) {
                    info.setRemark(info.getRemark() + ",超区调度费:" + disPatchData.getAreaMoney() / 100 + "元");
                }
                if (disPatchData.getParkMoney() != null) {
                    info.setRemark(info.getRemark() + ",站外调度费:" + disPatchData.getParkMoney() / 100 + "元");
                }
                if (disPatchData.getHelmetMoney() != null) {
                    info.setRemark(info.getRemark() + ",头盔管理费:" + disPatchData.getHelmetMoney() / 100 + "元");
                }
                /**豁免金额存在，即调度费不收取*/
                if (info.getDiscountMoney() != null && info.getDiscountMoney() > 0) {

                    info.setRemark(info.getRemark() + ",调度费豁免:" + NumberUtil.div(info.getDiscountMoney().intValue(), 100) + "元");
                    info.setDispatchMoney(dispatchMoney);
                    /**添加豁免次数*/
                    UserDiscountStat userDiscountStat = new UserDiscountStat();
                    userDiscountStat.setUserId(user.getUserId());
                    userDiscountStat.setAdAccountId(info.getAccountId());
                    userDiscountStat.setType(info.getDiscountType());
                    userDiscountStatService.insert(userDiscountStat);

                    Double discountMoney = info.getDiscountMoney().doubleValue();
                    dispatchMoney = NumberUtil.sub(dispatchMoney, discountMoney);
                    map.put("real", dispatchMoney);
                    realCashMoney = realCashMoney + dispatchMoney;
                    rideLogDetails.setDeductionFineMoney(dispatchMoney.longValue());
                } else {
                    /**兼容调度费用可用礼品卡抵扣，兼容有优惠券情况*/
                            /*if (money < dispatchMoney) {
                                dispatchMoney = money;
                            }*/
                    /**扣费*/
                            /*Double realMoney = handelRealMoney(info, userMoney, dispatchMoney, button == 1, giftCardUser
                            ,user,rideLog,true);*/

                    Double realMoney = handleMoney(userMoney, dispatchMoney, button == 1, giftCardUser, map);
                    /**实际扣除的余额,调度记录只记录余额扣费*/
                    if (dispatchMoney > realMoney) {
                        info.setDispatchMoney(realMoney);
                    }
                    realCashMoney = realCashMoney + realMoney;
                    /**用户可用余额*/
                    userMoney = userMoney - realMoney;
                    rideLogDetails.setDeductionFineMoney(realMoney.longValue());
                    /**计算虚拟金额抵扣费用*/
                    gifMoney = gifMoney + dispatchMoney.intValue() - realMoney.intValue();
                }
                money = money - dispatchMoney;
            }

            if (money > 0) {

                info.setRemark(info.getRemark() + ",正常骑行扣费:" + money / 100 + "元");
                //Double realMoney = handelRealMoney(info, userMoney, money, button == 1, giftCardUser,user,rideLog,false);
                Double realMoney = handleMoney(userMoney, money, button == 1, giftCardUser, map);
                rideLogDetails.setDeductionCyclingMoney(realMoney.longValue());
                /**计算虚拟金额抵扣费用*/
                gifMoney = gifMoney + money.intValue() - realMoney.intValue();
                realCashMoney = realCashMoney + realMoney;
            }
            deduction(map, info, user, rideLog, giftCardUser, payTime);
        }
        /**骑行卡超时*/
        /*if (info.isRideOvertime()) {
         *//**累计骑行次数*//*
                rideCardUserService.updateUseCount(info.getRideCardOrderNO());
            }
            *//**骑行卡减免费用金额不为0，代表使用骑行卡*//*
            if (info.getRidecardMoney() != null) {
                *//**累计骑行次数*//*
                rideCardUserService.updateUseCount(info.getRideCardOrderNO());
            }*/
        if (info.getRideCardOrderNO() != null) {
            rideCardUserService.updateUseCount(info.getRideCardOrderNO());
        }
        /*} else {
         *//**累计骑行次数*//*
            rideCardUserService.updateUseCount(info.getRideCardOrderNO());
            *//**记录订单详细明细*//*
         *//* RideLogDetail rideLogDetail = new RideLogDetail();
            rideLogDetail.setMoney(0);
            rideLogDetail.setOrderNO(info.getOrderNO());
            rideLogDetail.setBusType(RideLogDetailConstant.DETAILLOG_RIDECARD);
            rideLogDetail.setRemark("骑行卡免费骑行");
            rideLogDetailService.insert(rideLogDetail);*//*
        }*/
        if (info.getEndTime() == null) {
            rideLog.setEndTime(DateTimeUtil.getNowTime());
        }
        //更新骑行日志
        rideLog.setEndTime(info.getEndTime());

        if (info.getPayTime() != null) {
            payTime = info.getPayTime();
        }
        if (info.getLessMoney() == 0) {
            rideLog.setPayTime(payTime);//设置支付时间
            /**记录余额支付金额*/
            Integer rechargeType = null;
            Double otherMoney = 0D;
            RechargeMerchant rechargeMerchant = null;
            if (info.getPayMoney() != null) {
                otherMoney = info.getPayMoney();
                rechargeType = info.getOrderType();
                rechargeMerchant = rechargeMerchantService.getByOrderNO(info.getMerchantNO());
            }
            rideUseLogService.insert(rideLog.getOrderNO(), rideLog.getAccountId(), user.getAccountId(),
                    realCashMoney.intValue(), realCashMoney.intValue() - otherMoney.intValue(), rechargeType,
                    DateTimeUtil.StringToDateTime(rideLog.getPayTime()), rechargeMerchant);
            /**兼容自动退押金功能*/
            Boolean deposit = Optional.ofNullable(cacheService.getAccountConfigExtByKey(rideLog.getAccountId()
                    , AccountConfigExtConstant.ACCOUNTCONFIG_AUTO_DEPOSIT)).map(x -> "1".equalsIgnoreCase(x.getParamValue())).orElse(false);
            if (deposit && user.getDepositMoney() > 0) {
                DepositData depositData = new DepositData(user.getUserId(), user.getAccountId());
                mqProducer.sendToRide(GatewayProtocol.EXCHANGE_PAY_PUSH, RabbitMQData.returndepositkey, JSON.toJSONString(depositData));
            }
        } else {
            User userDB = new User();
            userDB.setRideMoney(realCashMoney);
            userDB.setUpdateTime(payTime);
            userDB.setUserId(user.getUserId());
            userService.updateRideMoney(userDB);
            Account account = accountService.getByAccountId(rideLog.getAccountId());
            /**欠款推送日志*/
            ArrearsLog arrearsLog = new ArrearsLog(user.getUserId(), user.getPhone(), user.getName(), rideLog.getAccountId(), user.getAccountId()
                    , account.getName(), rideLog.getOrderNO(), ArrearsLogContent.ARREARS, ArrearsLogContent.RIDING_ARREARS
                    , realCashMoney.intValue(), 0, realCashMoney.intValue(), DateTimeUtil.StringToDateTime(payTime).getTime(), "", rideLogDetails.getOperatorId()
                    , rideLogDetails.getOperatorName(), "", "");
            pushMsgLogService.sendPushLog(JSON.toJSONString(arrearsLog), TableNameConstant.ARREARSLOG);
            /**更新用户区域欠款*/
            userAccountMoneyService.insert(user, realCashMoney.longValue(), BalanceLogChangeTypeContent.capitalType_arrearsLog
                    , rideLog.getAccountId(), PayPlatformContent.PAY_RECHARGE);
        }
        rideLog.setMileage(info.getMileage());
        /**记录金额加上调度金额*/
				/*if(info.getDispatchMoney()!=null){
					money=money+info.getDispatchMoney();
				}*/
        rideLog.setMoney(realCashMoney);
        rideLog.setEndPointId(info.getParkPointId());
        rideLog.setRemark(info.getRemark());
        rideLogService.updateByOrderNOSelective(rideLog);

        List<CardUseLog> cardUseLogs = new ArrayList<>();
        if (info.getRideCardOrderNO() != null) {
            RideCard rideCard = rideCardService.getByOrderNO(info.getRideCardOrderNO());
            CardUseLog cardUseLog = new CardUseLog();
            cardUseLog.setAccountId(user.getAccountId());
            cardUseLog.setUseAccountId(rideLog.getAccountId());
            cardUseLog.setBuyAccountId(rideCard.getAccountId());
            cardUseLog.setAddTime(rideLog.getPayTime() == null ? new Date() : DateTimeUtil.StringToDateTime(rideLog.getPayTime()));
            cardUseLog.setCardId(rideCard.getRideCardId());
            cardUseLog.setCardType(CardTypeConstant.TYPE_RIDECARD);
            cardUseLog.setCardName(rideCard.getName());
            cardUseLog.setUserId(user.getUserId());
            cardUseLog.setMoney((int) (rideCard.getMoney() / rideCard.getMaxRide()));
            cardUseLog.setOrderNO(rideLog.getOrderNO());
            cardUseLogs.add(cardUseLog);
        }
        if (!cardUseLogs.isEmpty()) {
            cardUseLogDao.addBatch(cardUseLogs);
        }
        rideLogDetails.setMerchantNO(info.getMerchantNO());
        rideLogDetails.setBillingDetails(info.getRemark());
        rideLogDetails.setGiftCardDeductionMoney(gifMoney.longValue());
        rideLogDetails.setDeductionEndMoney(rideLogDetails.getDeductionEndMoney() - gifMoney);
        rideLogDetails.setActualPayMoney(realCashMoney.longValue());
        rideLogDetails.setPayTime(DateTimeUtil.StringToDateTime(payTime));
        rideLogDetails.setPayStatus(RideLogDetailsConstant.ORDER_PAY);
        if (info.getLessMoney() > 0) {
            rideLogDetails.setOrderType(RideLogDetailsConstant.ORDER_TYPE_ARREARS);//
            rideLogDetails.setActualPayMoney(0L);
            rideLogDetails.setPayTime(null);
            rideLogDetails.setPayStatus(RideLogDetailsConstant.ORDER_ARREARS);
        }
        //获取还车时候订单所在的分区信息
        GeoReMachine geoReMachine = geoReMachineDao.getByMachineId(info.getMachineId());
        Integer regionId = Optional.ofNullable(geoReMachine).map(GeoReMachine::getRegionId).orElse(-1);
        ParkPoint parkPoint = parkPointService.getByParkPointId(info.getParkPointId());
        if (parkPoint != null && Objects.nonNull(parkPoint.getRegionId())) {
            regionId = parkPoint.getRegionId();
        }
        rideLogDetails.setRegionId(regionId);
        /**推送日志*/
        pushMsgLogService.sendPushLog(JSON.toJSONString(rideLogDetails), TableNameConstant.RIDELOGDETAILS);

        ridePayLogService.del(rideLog.getOrderNO());
        String serNODB = redisService.get(RedisConstant.USER_FINISH_PAY + user.getUserId());
        if (serNODB != null) {
            redisService.del(serNODB);
            /**清除缓存*/
            redisService.del(RedisConstant.USER_FINISH_PAY + user.getUserId());
        }
        redisService.del(RedisConstant.DEVIATION_KEY_PREFIX + user.getUserId());
        /**清除道钉缓存*/
        String tmp = redisService.get(RedisConstant.MACHINE_NO_BLEINFO + info.getMachineNO());
        if (tmp != null) {
            redisService.del(RedisConstant.MACHINE_NO_BLEINFO + info.getMachineNO());
        }
        /**清除缓存*/
        redisService.del(RedisConstant.FORCE_HELMET_KEY_PREFIX + info.getMachineNO());
        redisService.del(RedisConstant.FORCE_SXT_KEY_PREFIX + info.getMachineNO());
        redisService.del(RedisConstant.MACHINE_BORROW_EX + info.getMachineNO());
        redisService.del(RedisConstant.RFID_FAIL_KEY_PREFIX + info.getMachineNO());
        redisService.del(RedisConstant.ANGLE_FAIL_KEY_PREFIX + info.getMachineNO());
        redisService.del(RedisConstant.WEBCAM_FAIL_KEY_PREFIX + info.getMachineNO());
        redisService.del(RedisConstant.JC_FAIL_KEY_PREFIX + info.getMachineNO());
        redisService.del(RedisConstant.CDZ_FAIL_KEY_PREFIX + info.getMachineNO());
        redisService.del(RedisConstant.PHONE_FAIL_KEY_PREFIX + info.getMachineNO());
        /**还车信息推送mq到数据推送平台*/
        String pointMsg = redisService.get(RedisConstant.MACHINE_RETURN_LOCAL + info.getMachineNO());
        try {
            Integer type = DisPatchConstant.DISPATCH_PARK;
            if (info.getDispatchMoney() != null) {
                type = DisPatchConstant.DISPATCH_NO_IN_PARK;
            }

            Point point = new Point(0.0, 0.0);
            if (StrUtil.isNotEmpty(pointMsg)) {
                point = JSON.parseObject(pointMsg, Point.class);
            }
            String bleMsg = redisService.get(RedisConstant.MACHINE_BLE + info.getMachineNO());
            String rfidMsg = redisService.get(RedisConstant.MACHINE_RFID + info.getMachineNO());

            /*
                满足任一条件，运维调度订单判断有效，否则无效
                    1、金额：订单金额/礼品卡抵扣/卡券抵扣不为0
                    2、时长：骑行时长大于等于3分钟
            */
            Long cardDeductionMoney = Optional.ofNullable(rideLogDetails.getCardDeductionMoney()).orElse(0L);
            Long giftCardDeductionMoney = Optional.ofNullable(rideLogDetails.getGiftCardDeductionMoney()).orElse(0L);
            LocalDateTime startDateTime = DateUtil.parseLocalDateTime(rideLog.getEndTime());
            LocalDateTime endDateTime = DateUtil.parseLocalDateTime(rideLog.getStartTime());
            long rideTime = Duration.between(startDateTime, endDateTime).getSeconds();
            boolean mainValid = money != 0 || cardDeductionMoney != 0 || giftCardDeductionMoney != 0 || rideTime >= 180;

            RidePushEntity ridePushEntity = new RidePushEntity(info.getOrderNO(), info.getUserCode(), info.getMachineNO(), bleMsg, rfidMsg, info.getAccountId(), user.getPhone(),
                    point.getX(), point.getY(), rideLog.getEndTime(), info.getMileage(), money.intValue(), 1, type, mainValid);
            MqData<RidePushEntity> mqData = new MqData<>();
            mqData.setMsgId(GatewayProtocol.MQ_MSG_ID_AROUND_RIDE_PUSH);
            mqData.setFeedback(RabbitMQData.ridequeue);
            mqData.setData(ridePushEntity);
            terControlService.sendPushMsg(RabbitMQData.ridekey, JSON.toJSONString(mqData));
        } catch (Exception e) {
            log.error("数据推送失败", e);
        }


        /**新用户邀请活动赠送*/
        couponCheck(info, rideLog, user);


        /**记录停车点*/
        /**调度车辆，忽略*/
        if (info.getParkPointId() != null && info.getParkPointId() != -1 && info.getParkPointId() != -2 && info.getParkPointId() != -3) {
            ParkMachine parkMachine = new ParkMachine();
            /**删除上一次停车点绑定，更新新一次停车点绑定**/
            /*parkMachineService.del(info.getMachineId()); 已通过定时任务删除 */
            parkMachine.setParkPointId(info.getParkPointId());
            parkMachine.setMachineId(info.getMachineId());
            parkMachine.setMachineNO(info.getMachineNO());
            parkMachine.setAddTime(DateTimeUtil.getNowTime());
            parkMachine.setInside(1);
            parkMachineService.insert(parkMachine);
        }

        /**调度车辆记录调度日志*/
        if ((info.getParkPointId() == null || info.getParkPointId() == -1) && info.getDispatchMoney() != null && info.getDispatchMoney() > 0) {
            DispatchLog dc = new DispatchLog();
            dc.setAccountId(rideLog.getAccountId());//代理商id,非品牌id
            dc.setLat(info.getLa());
            dc.setLon(info.getLo());
            dc.setUserCode(info.getUserCode());
            dc.setMachineNO(info.getMachineNO());
            dc.setOrderNO(info.getOrderNO());
            dc.setReturnTime(DateTimeUtil.getNowTime());
            if (info.getDispatchMoney() != null) {
                dc.setMoney(info.getDispatchMoney());
            } else {
                dc.setMoney(0D);
            }
            /**分区车辆需要添加分区绑定*/
            if (geoReMachine != null) {
                dc.setRegionId(geoReMachine.getRegionId());
            }
            dc.setPay(rideLog.getPayTime() != null);
            dispatchLogService.insert(dc);
        }

        //获取还车时候订单所在的分区信息
        RideLog rideLogRegion = new RideLog();
        rideLogRegion.setOrderNO(info.getOrderNO());
        rideLogRegion.setRegionId(regionId);
        rideLogService.editRegionId(rideLogRegion);

        /**关锁服务通知*/
        AccountConfigExt accountConfigExt = cacheService.getAccountConfigExtByKey(info.getAccountId(),
                AccountConfigExtConstant.ACCOUNTCONFIG_MODEL);
        if (accountConfigExt != null && "1".equals(accountConfigExt.getParamValue())) {
            Map<String, String> map = new HashMap<>();
            Date start = DateTimeUtil.StringToDateTime(rideLog.getStartTime());
            Date end = DateTimeUtil.StringToDateTime(rideLog.getEndTime());
            long rideTime = (((end.getTime() - start.getTime()) / 1000) / 60);
            if ((((end.getTime() - start.getTime()) / 1000) % 60) > 0) {
                rideTime = rideTime + 1;
            }
            map.put("thing1", "骑行" + rideTime + "分钟");
            map.put("time2", rideLog.getStartTime());
            map.put("time3", rideLog.getEndTime());
            map.put("thing4", "欢迎下次再次骑行！");
            weixinPayService.sendLockMsg(user.getAccountId(), user.getUserId(), map, WeixinConstant.TEMPLATE_TYPE_LOCK, null);
            // 订单待支付服务消息推送
            User userDb = userService.getByUserId(user.getUserId());
            log.info("用户欠款金额{}", userDb.getRideMoney());
            if (userDb.getRideMoney() > 0) {
                wxPushService.pushNotPayOrderMsg(rideLog.getOrderNO());
            }
        }
        //计算用户骑行分
        computeUserRidingScore(rideLogDetails.getUserId(), rideLogDetails.getFineType(), rideLogDetails.getAccountId(), rideLogDetails.getBrandId());
        Machine machine = new Machine();
        machine.setAccountId(rideLog.getAccountId());
        machine.setUserCode(info.getUserCode());
        machine.setMachineNO(info.getMachineNO());
        Integer endPointId = rideLog.getEndPointId();
        //todo -1的含义是？
        if (endPointId != null && endPointId == -1) {
            endPointId = null;
        }
        returnBikeMsgLogService.addBikeMsgLog(mb, machine, ReturnBikeMsgLogConstant.POS_FROM_MACHINE, ReturnBikeMsgLogConstant.TYPE_ACTUAL, endPointId, null, rideLogDetails.getOrderEndTime(), new Point());
        if (info.getStopTime() != null) {
            // 更新临停时长
            rideLogService.updateStopTimeByOrderNO(rideLog.getOrderNO(), info.getStopTime());
        }
        //判断车辆是否有临时停车记录，如果有，则删除
        parkService.delByUserId(info.getUserId());

        //判断骑行订单是否有效
        boolean validRide = validRide(rideLogDetails, rideLog, money);
        // 检查是否需要记录体重
        if (userDetailService.shouldLogWeight(machine.getAccountId(), mb.getMachineId())) {
            doLogWeight(info, user, validRide);
        }

        //检查是否需要记录车辆为疑似故障车辆
        this.checkSuspectedFault(rideLog, info.getLo(), info.getLa());
    }


    /**
     * 添加扣费记录
     *
     * @param user         用户信息
     * @param orderNO      骑行订单信息
     * @param dateTime     交易时间戳
     * @param capitalType  金额类型
     * @param businessType 交易类型
     * @param startMoney   开始金额
     * @param changeMoney  变动金额
     * @param remark       备注
     */
    @Override
    public void addBalanceChange(User user, String orderNO, Integer accountId, Long dateTime, Integer capitalType, Integer businessType
            , Long startMoney, Long changeMoney, String remark, Integer changeType) {

        userAccountMoneyService.editMoney(user, orderNO, changeMoney, capitalType, accountId
                , changeType, dateTime, remark, businessType);
    }


    @Override
    public void ter_lock(String serNO) {
        long startTime = System.currentTimeMillis();
        String brtInfo = redisService.get(serNO);
        if (brtInfo != null) {

            BorrowReturnTer info = JSONObject.parseObject(brtInfo, BorrowReturnTer.class);
            handlePay(info);
            /**兼容余额支付分账*/
            String fees = redisService.get(RedisConstant.USER_CASH_FEE + serNO);
            if (fees != null) {
                //userDelayPayService.add(JSON.parseObject(fees, UserDelayPay.class));
                mqProducer.sendToRide(GatewayProtocol.EXCHANGE_PAY_PUSH, RabbitMQData.cashkey, fees);
                redisService.del(RedisConstant.USER_CASH_FEE + serNO);
            }
            // 只有余额大于费用才需要退余额
            if (info.getLessMoney() == 0 && info.getType() == 0) {
                /**执行自动退余额操作*/
                // 判断是否充值结束订单或欠款补交或 骑行消费余额 ，开启骑行结束自动退余额
                if (cacheService.configValid(info.getAccountId(), AccountConfigExtConstant.AUTO_REFUND_BALANCE)) {
                    String refundModel = refundSwitchConfig.getRefundModel();
                    User user = userService.getByUserId(info.getUserId());
                    if (user.getMoney() > 0) {
                        if (Objects.equals(refundModel, RefundSwitchConfig.REFUND_NEW)) {
                            // 当前退款批次号，仅用于查询同一次请求的分账信息
                            String refundBatch = System.currentTimeMillis() + String.format("%06d", ThreadLocalRandom.current().nextInt(0, 1000000));
                            // 新退款接口 配置了新接口才为新退款，否则走老退款
                            userService.returnMoneyNew(info.getAccountId(), user.getMoney(), user.getUserId(), true, null, "系统", "13800000001", refundBatch);
                        } else {
                            // 老退款接口
                            userService.returnMoney(info.getAccountId(), user.getMoney(), user.getUserId(), true, null, "系统", "13800000001");
                        }
                    }
                }
            }

            // 订单结束判断用户存在欠款,则进行欠款服务消息推送
//            User user = userService.getByUserId(info.getUserId());
//            log.info("用户欠款金额{}，类型{}",user.getRideMoney(),info.getType());
//            if (info.getType() == 0 && user.getRideMoney() > 0){
//                wxPushService.pushNotPayOrderMsg(info.getOrderNO());
//            }

            log.info(info.getOrderNO() + "上锁业务处理时间[" + (System.currentTimeMillis() - startTime) + "]ms");
        }

    }


    /**
     * 预扣费，生成要扣费金额
     *
     * @param userMoney
     * @param cashMoney
     * @param isReal
     * @param giftCardUser
     * @param map
     * @return
     */
    public Double handleMoney(Double userMoney, Double cashMoney, boolean isReal,
                              GiftCardUser giftCardUser, Map<String, Double> map) {

        Double realMoney = map.get("real");
        Double gifMoney = map.get("gift");
        /**兼容超时订单等、金额不足扣费情况*/
        if (userMoney.intValue() + giftCardUser.getMoney().intValue() < cashMoney.intValue()) {
            Integer gifCardMoney = giftCardUser.getMoney();
            if (gifCardMoney > 0) {
                giftCardUser.setMoney(0);
                map.put("gift", gifMoney + gifCardMoney);
            }
            return cashMoney - gifCardMoney;
        }
        if (isReal) {
            if (userMoney >= cashMoney) {
                /**扣取费用*/
                map.put("real", realMoney + cashMoney);
                return cashMoney;
            } else {
                Double money = cashMoney;
                /**扣取费用*/
                if (userMoney > 0) {
                    map.put("real", realMoney + userMoney);
                    money = cashMoney - userMoney;
                }
                if (DoubleUtil.parseDouble(money) > 0) {
                    map.put("gift", gifMoney + money);
                    giftCardUser.setMoney(giftCardUser.getMoney() - money.intValue());
                }
                return userMoney;
            }
        } else {
            if (giftCardUser != null) {
                if (giftCardUser.getMoney() >= cashMoney) {
                    map.put("gift", gifMoney + cashMoney);
                    giftCardUser.setMoney(giftCardUser.getMoney() - cashMoney.intValue());
                    return 0D;
                } else {
                    Integer giftCardUserMoney = giftCardUser.getMoney();
                    if (giftCardUser.getMoney() > 0) {
                        giftCardUser.setMoney(0);
                        map.put("gift", gifMoney + giftCardUserMoney);
                    }
                    Double money = cashMoney - giftCardUserMoney;
                    map.put("real", realMoney + money);
                    return money;
                }
            } else {
                map.put("real", realMoney + cashMoney);
                return cashMoney;
            }
        }

    }

    /**
     * 扣费
     *
     * @param map
     * @param info
     * @param user
     * @param rideLog
     * @param giftCardUser
     */
    public void deduction(Map<String, Double> map, BorrowReturnTer info, User user, RideLog rideLog, GiftCardUser giftCardUser
            , String payTime) {
        Double realMoney = map.get("real");
        Double gifMoney = map.get("gift");
        /**兼容充值结束订单不扣余额*/

        if (realMoney > 0 && info.getLessMoney() == 0) {
            cashRealMoney(info, realMoney, user, rideLog, payTime);
        }
        if (gifMoney > 0) {
            cashGif(giftCardUser, gifMoney, info, user, rideLog, payTime);
        }
    }

    /**
     * @param info
     * @param cashMoney
     */
    public void cashRealMoney(BorrowReturnTer info, Double cashMoney, User user, RideLog rideLog, String payTime) {
        Long startMoney = user.getMoney().longValue();
        /**扣取费用*/
        userService.updateMoney(info.getUserId(), -cashMoney);
        if (info.getPayMoney() != null) {
            cashMoney = cashMoney - info.getPayMoney();
        }
        if (cashMoney == 0) {
            return;
        }
        String remark = "余额扣费" + cashMoney / 100 + "元";
        cash(info, cashMoney, RechargeConstant.MONEY_TYPE_REAL, RideLogDetailConstant.DETAILLOG_BALANCE, remark);
        info.setRemark(info.getRemark() + "," + remark);
        addBalanceChange(user, rideLog.getOrderNO(), rideLog.getAccountId(), DateTimeUtil.StringToDateTime(payTime).getTime(), BalanceLogChangeTypeContent.capitalType_balance
                , BusinessConstant.BUSINESS_BALANCE_OF_CYCLING, startMoney, cashMoney.longValue(), remark, PayPlatformContent.PAY_USE);
    }

    /**
     * @param giftCardUser
     * @param cashMoney
     * @param info
     */
    public void cashGif(GiftCardUser giftCardUser, Double cashMoney, BorrowReturnTer info, User user, RideLog rideLog, String payTime) {
        //Long startMoney = giftCardUser.getMoney().longValue();
        giftCardUser.setMoney(-cashMoney.intValue());

        String remark1 = "礼品卡扣费:" + cashMoney / 100 + "元";
        cash(info, cashMoney, RechargeConstant.MONEY_TYPE_GIF, RideLogDetailConstant.DETAILLOG_GIFTCARD, remark1);
        giftCardUserService.insert(giftCardUser, remark1, BusinessConstant.BUSINESS_RIDE_USE_GIF, rideLog.getOrderNO(), payTime);

        info.setRemark(info.getRemark() + "," + remark1);

    }

    public void addUserCapitalChange(User user, String orderNO, Integer adAccountId, Integer capitalType, Integer businessType,
                                     String dateTime, String details, String remark, Integer originalAccountId, Long changeStartMoney, Long changeMoney, Long changeEndMoney) {
        UserCapitalChange userCapitalChange = new UserCapitalChange(user.getUserId(), user.getPhone(), user.getName(), adAccountId,
                user.getAccountId(), changeStartMoney, changeMoney, changeEndMoney, PayPlatformContent.PAY_RECHARGE, capitalType,
                businessType, details, null, null, null, orderNO, orderNO, DateTimeUtil.StringToDateTime(dateTime).getTime(),
                remark, originalAccountId);
        pushMsgLogService.sendPushLog(JSON.toJSONString(userCapitalChange), TableNameConstant.USERCAPITALCHANGE);
    }

    /**
     * 获取合适的优惠券
     *
     * @param info
     * @param user
     * @param money
     * @param giftCardMoney
     * @return
     */
    CouponUserDto couponUseCheck(BorrowReturnTer info, User user, double money, double giftCardMoney) {
        CouponUserDto couponUserDB = null;
        /**获取未使用的按金额从大到小顺序排序的优惠券*/
        List<CouponUserDto> list = couponUserService.getMoneyCoupon(info.getAccountId(), info.getUserId());
        /**存在金额优惠券,未使用时间优惠券*/
        if (list.size() > 0 && (info.getCouponUserDto() == null || info.getCouponUserDto().getCouponuserId() == null)) {
            for (CouponUserDto couponUserDto : list) {
                if (money <= couponUserDto.getCouponMoney()) {
                    couponUserDB = couponUserDto;
                } else {
                    /**优惠券最大金额比支付金额小*/
                    if (couponUserDB == null) {
                        /**优惠券加余额小于支付金额*/
                        if ((couponUserDto.getCouponMoney() + giftCardMoney + user.getMoney()) >= money) {
                            couponUserDB = couponUserDto;
                        }
                    }
                }
            }
        }
        return couponUserDB;
    }

    /**
     * 新用户赠送活动
     *
     * @param info
     * @param rideLog
     * @param user
     */
    void couponCheck(BorrowReturnTer info, RideLog rideLog, User user) {
        try {
            Integer time = DateTimeUtil.getTimeDF(rideLog.getStartTime(), rideLog.getEndTime());
            /**检验*/
            if (time <= 120) {
                return;
            }

            /**借车次数*/
            //Integer num = rideLogService.queryCount(info.getUserId());
            //if(num<=1){
            InvitationJoin invitationJoin = invitationJoinService.getByUserIdAndType(info.getUserId(),
                    InviteConstant.EVENT_INVITATION);
            if (invitationJoin != null && invitationJoin.getEndTime().getTime() > System.currentTimeMillis() && invitationJoin.getState() == NewEventConstant.INVITATIONJOIN_STATE_DEFAULT) {
					/*UserActiveLog userActiveLogDB =userActiveLogService.getByPhoneAndEventId(user.getPhone(), invitationJoin.getEventId(), NewEventConstant.REWARD_TYPE_NEW_USER_FINISH);
					if(userActiveLogDB!=null){
						log.info("已经触发赠送，不在赠送"+user.getPhone());
						return;
					}*/
                InviteNewEvent inviteNewEvent = inviteNewEventService.getByEventId(invitationJoin.getEventId());
                List<Integer> integerList = new ArrayList<>();
                integerList.add(NewEventConstant.REWARD_TYPE_NEW_USER_FINISH);
                integerList.add(NewEventConstant.REWARD_TYPE_INVITE_FINISH);
                if (inviteNewEvent != null) {
                    /**获取赠送详细*/
                    List<EventDetail> eventDetails = eventDetailService.getByType(inviteNewEvent.getInviteId(),
                            integerList);
                    if (eventDetails.size() == 0) {
                        return;
                    }

                    /**触发发出邀请用户赠送*/
                    for (EventDetail e : eventDetails) {
                        User inviteUser = user;
                        if (e.getType().equals(NewEventConstant.REWARD_TYPE_INVITE_FINISH)) {
                            inviteUser = userService.getByUserId(invitationJoin.getInviteUserId());
                            /**判断是否达到了赠送上限,达到赠送上限不在触发赠送*/
                            Integer count = invitationJoinService.getUserCount(invitationJoin.getInviteUserId(), invitationJoin.getEventId());
                            if (count > e.getFrequency()) {
                                log.warn("用户参与活动达到上限" + user.getPhone() + ",用户id=" + inviteUser.getUserId());
                                continue;
                            }
                        } else {
                            /**添加活动参数记录*/
                            //todo 用户活动奖励发放
                            activeStatService.add(inviteUser, inviteNewEvent.getInviteId(), NewEventConstant.EVENT_TYPE_FINISH);
                        }
                        //todo 用户首次骑行赠送
                        inviteNewEventService.couponGive(e, inviteUser, rideLog.getAccountId(), new Date(), inviteNewEvent.getName());
                    }
                    /**更新赠送记录*/
                    InvitationJoin invitationJoinDB = new InvitationJoin();
                    invitationJoinDB.setId(invitationJoin.getId());
                    invitationJoinDB.setState(NewEventConstant.INVITATIONJOIN_STATE_SUCCESS);
                    invitationJoinDB.setRideTime(new Date());
                    invitationJoinService.edit(invitationJoinDB);
                }
            }
            //}
            /**兼容马蹄锁号段不推送*/
            if (!info.getUserCode().startsWith("901") && !info.getUserCode().startsWith("902")) {
                /**借还车电量信息存放内存，定时推送*/
                pushVolMsg(info, rideLog.getOrderNO(), MachineStateConstant.MACHINE_TYPE_RETURN);
            }

            AccountConfig accountConfig = accountConfigService.getByAccountId(rideLog.getAccountId());
            /**模板消息推送开关是否打开*/
            if (accountConfig != null && accountConfig.getWxModelMsg() == 1) {
                /**发送模板消息*/
                UserWxzff userWxzff = userWxzffDao.getByUserId(rideLog.getUserId());
                WeixinConfig weixinConfig = weixinConfigDao.getByAccountId(user.getAccountId());
                WeixinPayConfig weixinPayConfig = weixinPayConfigService.getByAccountId(user.getAccountId(), 2);
                if (userWxzff != null && weixinConfig != null && weixinPayConfig != null) {
                    String weixinAccessToken = weixinPayService.getAccessToken(weixinPayConfig);
                    WxzffModelMsgUtil.sendTemplate(weixinAccessToken, userWxzff.getOpenId(), weixinConfig.getModelMsgId(), rideLog.getStartTime(), rideLog.getEndTime());
                }
            }
        } catch (Exception e) {
            log.error("赠送异常", e);
        }

    }


    /**
     * 记录扣费日志
     *
     * @param info      骑行信息
     * @param money     金额
     * @param moneyType 扣费类型
     * @param busType   业务类型
     * @param remark    备注
     * @return
     */
    public UserAccountLog cash(BorrowReturnTer info, double money, Integer moneyType, Integer busType, String remark) {
        UserAccountLog userAccountLog = new UserAccountLog();
        userAccountLog.setMoneyType(moneyType);
        /**记录扣费日志*/
        userAccountLog.setAccountId(info.getAccountId());
        userAccountLog.setMoney(money);
        userAccountLog.setUserId(info.getUserId());
        userAccountLog.setOperaTime(DateTimeUtil.getNowTime());
        userAccountLog.setRemark(remark);
        userAccountLog.setType(0);
        userAccountLogService.insert(userAccountLog);
        /**记录订单详细明细*/
        RideLogDetail rideLogDetail1 = new RideLogDetail();
        rideLogDetail1.setMoney((int) money);
        rideLogDetail1.setOrderNO(info.getOrderNO());
        rideLogDetail1.setBusType(busType);
        rideLogDetail1.setRemark(remark);
        rideLogDetailService.insert(rideLogDetail1);
        return userAccountLog;
    }

    /**
     * 设备是否在道钉
     *
     * @param machineNO
     * @return
     */
    @Override
    public Integer isInBleBeacon(String machineNO, Integer accountId, String orderNO) {
        Map<String, BleNode> map = new HashMap<>();
        List<String> no = dispatchConfigService.phoneBleCheck(map, machineNO, accountId);
        if (no != null && no.size() > 0) {
            List<BleBeacon> bleBeacons = bleBeaconDao.getByMachineNOs(no);
            if (bleBeacons.size() > 0) {
                for (BleBeacon b : bleBeacons) {
                    if (!accountId.equals(b.getAdAccountId())) {
                        continue;
                    }
                    Integer parkPointId = b.getParkPointId();
                    /**对站点是否开启道钉rssi值进行判断*/
                    ParkFeature parkFeature = parkFeatureService.getByParkPointId(parkPointId,
                            ParkFeatureConstant.PARKFEATURE_TYPE_BLE);
                    if (parkFeature != null && parkFeature.getEnable() == 1) {
                        /**rssi值设定格式为rssi=70*/
                        if (parkFeature.getPropert().contains("rssi")) {
                            BleNode bleNode = map.get(b.getMachineNO());
                            Integer parkLevel = Integer.valueOf(parkFeature.getPropert().substring(parkFeature.getPropert().indexOf("=") + 1));
                            /**如果设定的合法值要比道钉的小，则认为不对*/
                            if (parkLevel < Math.abs(Integer.valueOf(bleNode.getBleLevel()))) {
                                parkPointId = null;
                            }
                        }
                    }
                    if (parkPointId != null) {
                        BleNode bleNode = map.get(b.getMachineNO());
                        log.info(orderNO + ",手机等级=" + bleNode.getBleLevel());
                        return parkPointId;
                    }
                }
            }
        }

        Integer parkPointId = dispatchConfigService.machineBleCheck(machineNO, accountId, orderNO);
        return parkPointId;
    }

    /**
     * 是否在区域
     *
     * @param geos
     * @param accountId
     * @param checkPoint
     * @return
     */
    public Boolean isInArea(List<Geo> geos, Integer accountId, Point checkPoint, Integer machineId) {
        Geo geo = geos.get(0);
        if (geo.getGeoSwitch() == 1) {
            /**是否有配置调度费*/
            AdAccountFee adAccountFee = adAccountFeeService.getByAccountId(accountId, machineId);
            if (adAccountFee == null) {
                return false;
            } else {
                /**判断是否在运营区域内*/
                List<Point> points = GeoUtil.getPoints(geos.get(0).getPoints());
                Boolean isInArea = GeoUtil.IsPtInPoly(checkPoint, points);
                /**经纬度点在区域内*/
                if (!isInArea) {
                    log.info("设备不在区域,经纬度点=" + checkPoint);
                    return false;
                }
            }
        } else {
            return false;
        }
        return true;
    }


    /**
     * 获取站点车辆摆放线段集合与正北方向角度
     *
     * @param points
     * @return
     */
    @Override
    public double getParkAngle(String points) {
        String[] strings = points.split(";");
        /**只取前两个点*/
        String[] strings1 = strings[0].split(",");
        String[] strings2 = strings[1].split(",");
        MyLatLng myLatLng = new MyLatLng(Double.parseDouble(strings1[0]), Double.parseDouble(strings1[1]));
        MyLatLng myLatLng1 = new MyLatLng(Double.parseDouble(strings2[0]), Double.parseDouble(strings2[1]));
        return GeoUtil.getAngle(myLatLng, myLatLng1);
    }


    /**
     * 垂直停车判断
     *
     * @param accountConfig
     * @param machine
     * @param parkPointId
     * @param
     */
    @Override
    public Dispatch isVertical(AccountConfig accountConfig, Machine machine, Integer userId, String orderNO
            , Integer parkPointId, String tlvs, Integer isLast) {
        Dispatch dispatch = new Dispatch(DisPatchConstant.DISPATCH_PARK, null);
        if (accountConfig.getVerticalPark() == 1) {
            Map<String, Object> map = new HashMap<>();
            map.put("machineId", machine.getMachineId());
            map.put("functionType", MachineStateConstant.MACHINE_TYPE_VERTICAL);
            MachineFunction machineFunctionDB = machineFunctionDao.getByMachineId(map);
            /**开关打开*/
            if (machineFunctionDB != null && machineFunctionDB.getFunctionValue() == 1) {
                String angelString = redisService.get(FunctionConstant.VERTICAL + machine.getMachineNO());
                if (!StringUtils.isEmpty(tlvs)) {
                    angelString = tlvs;
                }
                if ((angelString != null && Double.parseDouble(angelString) >= 0 && Double.parseDouble(angelString) <= 3600) || "-2".equals(angelString)) {
                    Double angle = Double.parseDouble(angelString) / 10;
                    ParkPointVert parkPointVert = parkPointVertDao.getByParkPointId(parkPointId);
                    if (parkPointVert != null) {
                        double parkAngle = getParkAngle(parkPointVert.getPoints());
                        String num = redisService.get(RedisConstant.ANGLE_FAIL_KEY_PREFIX + machine.getMachineId());
                        if (num == null) {
                            num = "0";
                        }
                        /***还车角度在允许误差范围外不许还车,并且还车失败次数小于设置还车次数*/
                        Boolean result = isTrue(parkAngle, angle, accountConfig.getOffsetAngle());
                        String tmp = redisService.get(RedisConstant.LOG_SIGN + machine.getMachineNO());
                        if (tmp != null && !"-2".equals(angelString)) {
                            log.info("设备编号=" + machine.getMachineNO() + "," + orderNO + ",终端上传的角度=" + angle + ",平台角度=" + parkAngle + ",校验结果" + result + "还车次数" + num + ",站点id" + parkPointId);
                            redisService.del(RedisConstant.LOG_SIGN + machine.getMachineNO());
                            ParkPoint parkPoint = parkPointService.getByParkPointId(parkPointId);
                            /**计算差值*/
                            double diff = Math.abs(angle - parkAngle);
                            diff = Math.min(diff, 360 - diff);
                            GeoReMachine geoReMachine = geoReMachineDao.getByMachineId(machine.getMachineId());
                            VerticalParkLog verticalParkLog = new VerticalParkLog(orderNO, machine.getAccountId(), parkPointId
                                    , parkPoint.getName(), machine.getMachineNO(), result ? 1 : 0, Integer.valueOf(num), angle, parkAngle, diff, 1, Optional
                                    .ofNullable(geoReMachine).map(x -> x.getRegionId()).orElse(null));
                            verticalParkLogService.add(verticalParkLog);
                        }
                        if (isLast == 1) {
                            redisService.add(RedisConstant.ANGLE_FAIL_KEY_PREFIX + machine.getMachineId(),
                                    String.valueOf(Integer.valueOf(num) + 1), 300);
                        }
                        if (!result && Integer.valueOf(num) < accountConfig.getReturnTimes()) {
                            //String s=String.valueOf(Integer.valueOf(num)+1);
                            //redisService.add(RedisConstant.ANGLE_FAIL_KEY_PREFIX+machine.getMachineId(),s,600);
                            dispatch = new Dispatch(DisPatchConstant.DISPATCH_VERTICAL, null, null, null, parkAngle, angle);
                        }
                    }
                } else {
                    //这里注释的原因是因为要兼容中控有时候无法上报垂直停车角度的情况导致用户无法还车，因此出现该状况直接放行
                    //dispatch = new Dispatch(DisPatchConstant.DISPATCH_VERTICAL, null, null, null, -1D, -1D);
                    log.warn("编号=" + machine.getMachineNO() + "设备未获取到角度信息");
                }
            } else {
                log.warn("编号=" + machine.getMachineNO() + "设备未开启垂直停车");
            }
        }
        return dispatch;
    }


    /**
     * 是否在站点附近
     *
     * @param parkPointId
     * @param machineNO
     * @param mapType
     * @return
     */
    @Override
    public Boolean isNearPark(Integer parkPointId, String machineNO, Integer mapType) {
        ParkPoint parkPoint = parkPointService.getByParkPointId(parkPointId);
        MachineStatus machineStatus = machineStatusService.getByMachineNO(machineNO, mapType);
        if (machineStatus != null && machineStatus.getLon() != null && machineStatus.getLat() != null) {
            if (DateTimeUtil.getMinDiffer(machineStatus.getBatDt()) < 30) {
                List<Point> points = GeoUtil.getPoints(parkPoint.getPoints());
                Point point = new Point(machineStatus.getLon(), machineStatus.getLat());
                boolean is = GeoUtil.IsPtInPoly(point, points);
                if (is) {
                    return true;
                } else {
                    Double distince = GeoUtil.getDistance(point, points);
                    log.info("还车车辆距离站点距离=" + distince);
                    if (distince <= 500) {
                        return true;
                    } else {
                        return false;
                    }
                }

            }
        }
        return true;
    }

    @Override
    public double calculateDistance(Integer machineId1, Integer machineId2) {

        List<MachineStatus> machineStatuses = machineStatusConsumerService.getByMachineIds(Lists.newArrayList(machineId1, machineId2));
        if (CollectionUtil.isEmpty(machineStatuses) || machineStatuses.size() != 2) {
            log.error("车辆位置信息不全:{}", JSON.toJSONString(machineStatuses));
            throw new BaseException("车辆位置信息不全");
        }
        MachineStatus machineStatus1 = machineStatuses.stream().filter(machineStatus -> machineStatus.getMachineId().equals(machineId1)).findFirst().get();
        MachineStatus machineStatus2 = machineStatuses.stream().filter(machineStatus -> machineStatus.getMachineId().equals(machineId2)).findFirst().get();

        if (Objects.isNull(machineStatus1) || Objects.isNull(machineStatus2) ||
                Objects.isNull(machineStatus1.getLon())
                || Objects.isNull(machineStatus1.getLat())
                || Objects.isNull(machineStatus2.getLon())
                || Objects.isNull(machineStatus2.getLat())) {
            throw new BaseException("车辆位置信息不全");
        }
        return GeoUtil.getDistanceBy(machineStatus1.getLon(), machineStatus1.getLat(), machineStatus2.getLon(), machineStatus2.getLat());
    }

    @Override
    public void isSuccess(Integer userId, Double lon, Double lat, Integer mapType) {
        MachineBorrow machineBorrow = machineBorrowService.getByUserId(userId);
        if (machineBorrow == null) {
            throw new BaseException("-3006", "不存在借出记录");
        }
        RideLog rideLog = rideLogService.getByUserIdNotFinish(userId);
        if (rideLog == null) {
            throw new BaseException("-3006", "不存在借出记录");
        }
        Machine machine = getByMachineId(machineBorrow.getMachineId());

        /**头盔锁判断*/

        checkHelmet(machine, machineBorrow.getUserId(), machineBorrow.getOrderNO(), new HashMap<>(), 0);

        Integer parkPointId = null;
        AccountConfig accountConfig = accountConfigService.getByAccountId(machine.getAccountId());
        MachineStatus machineStatus = machineStatusService.getByMachineNO(machine.getMachineNO(),
                MapConstant.MAP_ORI);
        if (machineStatus == null || (machineStatus.getLon() == null || machineStatus.getLat() == null)) {
            throw new BaseException("-3029", "故障车辆，车辆还没有上线");
        }
        if (accountConfig != null && accountConfig.getBigParkPoint().equals(ConfigTypeConstant.ACCOUNT_CONFIG_BLE)) {
            Boolean isBleNode = false;
            /**生成流水号,预先下发上传道钉信号指令*/
            String serNO = UUID.randomUUID().toString();
            terControlService.sendControl(serNO, machine.getMachineNO(), ControlTypeConstant.CONTROL_TYPE_CONTROL, "51", "", machine, true, -1, userId, "");
            String tmp = redisService.get(RedisConstant.MACHINE_NO_BLEINFO + machine.getMachineNO());
            if (tmp != null) {
                BleInfo bleInfo = JSON.parseObject(tmp, BleInfo.class);
                if (bleInfo != null && ((System.currentTimeMillis() - bleInfo.getUpdateTime()) <= 30000)) {
                    isBleNode = true;
                }
            }
            /**设备不支持道钉*/
            Boolean result = dispatchConfigService.checkBle(machine.getMachineId());
            if (!result) {
                isBleNode = true;
            }
            if (isBleNode) {
                Point point = new Point(machineStatus.getLon(), machineStatus.getLat());
                /**判断车辆位置是否在站点*/
                if (parkPointId == null) {

                    parkPointId = parkPointService.checkInParkPoint(machine.getAccountId(), point, machine.getMachineId(), null);
                }
            }
        } else if (accountConfig != null && accountConfig.getBigParkPoint().equals(ConfigTypeConstant.ACCOUNT_CONFIG_PARKPOINT) || accountConfig.getBigParkPoint().equals(
                ConfigTypeConstant.ACCOUNT_CONFIG_BIG)) {

            Point point = new Point(machineStatus.getLon(), machineStatus.getLat());
            /**判断车辆位置是否在站点*/
            if (parkPointId == null) {

                parkPointId = parkPointService.checkInParkPoint(machine.getAccountId(), point, machine.getMachineId(), null);
            }
            /**车辆位置不在，在判断手机位置*/
            if (parkPointId == null && lon != null && lat != null) {
                Point point1 = new Point(lon, lat);

                parkPointId = parkPointService.checkInParkPoint(machine.getAccountId(), point1, machine.getMachineId(), null);
            }
            /**道钉绑定站点*/
            Integer blePointId = isInBleBeacon(machine.getMachineNO(), machine.getAccountId(), machineBorrow.getOrderNO());
            /**兼容站点绑定道钉，检测*/
            parkPointId = parkFeatureService.check(parkPointId, blePointId, machine.getMachineNO(), machine.getAccountId(), machineBorrow.getOrderNO());
        }


        if (parkPointId == null) {
            throw new BaseException("-3008", "请在停车点内还车");
        }
        //垂直停车判断
        isVertical(accountConfig, machine, userId, rideLog.getOrderNO(), parkPointId, null, 1);
    }

    @Override
    public List<MachineFunction> isSupport(String userCode) {
        Machine machine = getByUserCode(userCode);
        if (machine == null) {
            throw new BaseException("-30005", "设备不存在");
        }
        return machineFunctionDao.getAll(machine.getMachineId());
    }

    /**
     * 存放骑行电压信息
     *
     * @param info
     * @param orderNO
     */
    public void pushVolMsg(BorrowReturnTer info, String orderNO, Integer type) {
        MachineStatus machineStatus = machineStatusService.getByMachineNO(info.getMachineNO(),
                MapConstant.MAP_GOOGLE);
        RideBattery rideBattery = SystemData.pushMap.get(orderNO);
        /**用户借车电压推送*/
        if (rideBattery == null) {
            rideBattery = new RideBattery();
        }
        rideBattery.setAccountId(info.getAccountId());
        rideBattery.setAddTime(new Date());
        rideBattery.setMachineNO(info.getMachineNO());
        rideBattery.setOrderNO(orderNO);
        rideBattery.setUserCode(info.getUserCode());
        rideBattery.setMileage(info.getMileage());
        if (machineStatus != null && machineStatus.getLon() != null && machineStatus.getLat() != null) {
            if (type.equals(MachineStateConstant.MACHINE_TYPE_BORROW)) {
                rideBattery.setStartVol(machineStatus.getBatteryEU().doubleValue() / 1000);
            }
            if (type.equals(MachineStateConstant.MACHINE_TYPE_RETURN)) {
                rideBattery.setEndVol(machineStatus.getBatteryEU().doubleValue() / 1000);
            }
        }
        SystemData.pushMap.put(orderNO, rideBattery);
    }

    /**
     * 是否满足条件
     *
     * @param parkAngle
     * @param angle
     * @param offset
     * @return
     */
    public Boolean isTrue(double parkAngle, double angle, double offset) {
        if (360 - parkAngle < offset) {
            if ((0 <= angle && angle <= (parkAngle + offset - 360)) || (parkAngle - offset <= angle && angle <= 360)) {
                return true;
            }
        } else if (parkAngle < offset) {
            if ((0 <= angle && angle <= (parkAngle + offset)) || (360 + parkAngle - offset <= angle && angle <= 360)) {
                return true;
            }
        } else {
            if (Math.abs(parkAngle - angle) <= offset) {
                return true;
            }
        }
        return false;
    }

    /**
     * 头盔锁校验
     *
     * @param machine
     */
    @Override

    public Dispatch checkHelmet(Machine machine, Integer userId, String orderNO, Map<String, Object> rideMap, Integer isLast) {
        Dispatch dispatch = null;

        //兼容乐摇摇车辆状态主动上传逻辑,乐摇摇车辆状态第17位为头盔锁，泰比特第12位为头盔锁,泰比特头盔1标识未锁上，乐摇摇1表示锁上了
        Boolean isLyyMachine = Optional.ofNullable(terInfoDao.getByMachineNO(machine.getMachineNO()))
                .map(terInfo -> "LYY".equalsIgnoreCase(terInfo.getFactoryCode())).orElse(false);
        /***/
        String startStatus = redisService.getWay(RedisConstant.MACHINE_NO_HELMET + userId);
        boolean borrow = false;
        if (startStatus != null) {
            borrow = ObjectUtil.statusParse(11, Integer.valueOf(startStatus));
        }

        boolean last = false;
        Map<String, Object> param = new HashMap<>();
        param.put("machineId", machine.getMachineId());
        List<Integer> list = new ArrayList<>();
        list.add(MachineStateConstant.MACHINE_TYPE_HELMAT);
        list.add(MachineStateConstant.MACHINE_TYPE_BLE_HELMET);
        list.add(MachineStateConstant.MACHINE_TYPE_INTEL_HELMET);
        param.put("list", list);
        List<MachineFunction> functions = machineFunctionDao.getHelmet(param);
        AdAccountFee adAccountFee = cacheService.getAdAccountFee(machine.getAccountId(), machine.getMachineId());
        int helmetMoney = adAccountFee == null ? 0 : adAccountFee.getHelmetMoney();
        rideMap.put("helmetSwitch", adAccountFee == null ? 0 : adAccountFee.getHelmetSwitch());
//        log.info("车辆设置的头盔锁功能：{}",JSON.toJSONString(functions));
        if (functions.size() > 0) {
            String tmp = redisService.get(FunctionConstant.CARSTATUS + machine.getMachineNO());
            if (isLyyMachine) {
                //兼容乐摇摇设备，开头盔锁按钮到还车时间不超出20秒，认为车辆状态无效
                TerAttrEx byMachineNO = terAttrExService.getByMachineNO(machine.getMachineNO(), TerAttrExConstant.ATTREX_STATUS);
                String openTime = Optional.ofNullable(redisService.get(RedisConstant.USER_OPEN_HELMET + machine.getMachineId()))
                        .orElse(DateUtil.now());
                long time = DateUtil.between(DateUtil.parse(openTime), new Date(), DateUnit.SECOND);
                if (byMachineNO != null && time > 10) {
                    tmp = byMachineNO.getValue();
                }
            }

            log.info("车辆状态={}", tmp);
            rideMap.put("carstatus", tmp);
            if (StringUtils.isEmpty(tmp)) {
                return new Dispatch(DisPatchConstant.DISPATCH_HELMET, null, helmetMoney, DisPatchConstant.DISPATCH_HELMET);
            }
            Integer terStatus = Integer.valueOf(tmp);
            Boolean isClose = ObjectUtil.statusParse(11, terStatus);
            for (MachineFunction fun : functions) {
                switch (fun.getFunctionType()) {
                    case MachineStateConstant.MACHINE_TYPE_HELMAT:
                        /**头盔锁打开*/
                        if (isClose) {
                            dispatch = new Dispatch(DisPatchConstant.DISPATCH_HELMET, null, helmetMoney, DisPatchConstant.DISPATCH_HELMET);
                            last = true;
                        }
                        break;
                    case MachineStateConstant.MACHINE_TYPE_BLE_HELMET:
                        /**蓝牙头盔锁*/
                        String tmpMsg = redisService.get(RedisConstant.RIDELOG_BEFORE_BORROW_STATUS + machine.getMachineNO());
                        //Integer beforeStatus = tmpMsg==null?0:Integer.valueOf(tmpMsg);
                        borrow = tmpMsg == null ? false : Integer.valueOf(tmpMsg) == 1;
                        if (terStatus == 0) {
                            dispatch = new Dispatch(DisPatchConstant.DISPATCH_HELMET, null, helmetMoney, DisPatchConstant.DISPATCH_HELMET);
                            last = true;
                        }
                        break;
                    case MachineStateConstant.MACHINE_TYPE_INTEL_HELMET:

                        if (isClose) {
                            dispatch = new Dispatch(DisPatchConstant.DISPATCH_HELMET, null, helmetMoney, DisPatchConstant.DISPATCH_HELMET);
                            last = true;
                        }
                        break;

                }
            }

            /**是否开启ex校验*/
            Boolean openHelmet = false;
            AccountConfigExt accountConfigExt = cacheService.getAccountConfigExtByKey(machine.getAccountId(), AccountConfigExtConstant.ACCOUNTCONFIG_OPEN_DEVICESTATUSEX);
            if (accountConfigExt != null && accountConfigExt.getParamValue().equalsIgnoreCase("1")) {
                openHelmet = true;
            }
            /**兼容车辆状态扩展指令*/
            String carStatus = redisService.get(FunctionConstant.CARSTATUSEX + machine.getMachineNO());
            rideMap.put("carStatusEx", carStatus);
            log.info("last={},openHelmet={},carStatus={}", last, openHelmet, carStatus);
            /***/
            if (openHelmet && StringUtils.isNotEmpty(carStatus)) {
                Integer status = Integer.valueOf(carStatus);
                if (ObjectUtil.statusParse(3, status) && !ObjectUtil.statusParse(2, status)) {
                    if (redisService.setNx(RedisConstant.HELMET_COMMAND_LOCK + orderNO, "", 10)) {
                        log.info("下发关闭头盔指令");
                        String serNO = UUID.randomUUID().toString();
                        terControlService.sendControl(serNO, machine.getMachineNO(), ControlTypeConstant.CONTROL_TYPE_CONTROL, ControlTypeConstant.CONTROL_TYPE_CLOSE_HELMET
                                , "", machine, false, -1, userId, "系统自动下发关闭头盔锁");
                        // 延迟2s后查询最新头盔锁状态
                        taskExecutor.execute(() -> {
                            try {
                                Thread.sleep(1000);
                                dispatchConfigService.returnCheck(machine.getUserCode());
                            } catch (InterruptedException e) {
                                log.info("[还车调度] 获取最新状态异常定时任务异常", e);
                            }
                        });

                        dispatch = new Dispatch(DisPatchConstant.DISPATCH_HELMET_EX, null, helmetMoney, DisPatchConstant.DISPATCH_HELMET);
                    }
                }

            }

            log.info(orderNO + ",借车前状态" + borrow);
            /**兼容头盔锁状态一直未上报问题，如果借车与还车头盔锁状态为改变，认为可以还车*/
            if (startStatus != null && last == borrow) {
                dispatch = null;
            }
            /**兼容开锁前ex指令如果借车前为插销不在位，还车还车也不校验插销状态
             借车前插销在位，还车也要插销在位*/
            String value = redisService.get(RedisConstant.MACHINE_BORROW_EX + machine.getMachineNO());
            if (value != null && StringUtils.isNotEmpty(carStatus)) {
                Integer borrowValue = Integer.valueOf(value);
                Integer status = Integer.valueOf(carStatus);
                if (ObjectUtil.statusParse(3, borrowValue) && !ObjectUtil.statusParse(3, status)) {
                    dispatch = new Dispatch(DisPatchConstant.DISPATCH_HELMET, null, helmetMoney, DisPatchConstant.DISPATCH_HELMET);
                }
                if (ObjectUtil.statusParse(3, borrowValue) && ObjectUtil.statusParse(3, status)) {
                    dispatch = null;
                }
            }
            //最后一次检查，如果开启了头盔丢失检查并且头盔丢失了就记录一条头盔丢失日志
            /*if (dispatch != null && Objects.equals(dispatch.getType(), DisPatchConstant.DISPATCH_HELMET)
                    && Objects.equals(isLast, 1) && this.helmetLostCheckSwitch(machine.getAccountId())) {
                log.info("记录头盔丢失日志-------------------------------------dispatch：{},isLast:{}", JSON.toJSONString(dispatch), isLast);
                // 记录头盔丢失日志
                createOrderHelmetLostLog(machine, userId, orderNO);
            }*/

            redisService.add(RedisConstant.FORCE_HELMET_KEY_PREFIX + machine.getMachineNO(), tmp, 30 * 60);
        } else {
            log.info("头盔锁功能不支持" + machine.getMachineNO());
        }

        return dispatch;
    }

    private void createOrderHelmetLostLog(Machine machine, Integer userId, String orderNO) {
        //因前端会多次传递了isLast = 1，调用，需要对单个订单做去重，获取订单记录的头盔丢失日志数
        if (orderHelmetLostLogService.getCountByOrderNO(orderNO) > 0) {
            return;
        }
        OrderHelmetLostLog orderHelmetLostLog = new OrderHelmetLostLog();
        Account account = Optional.ofNullable(cacheService.getAccount(machine.getAccountId())).orElse(new Account());
        User user = Optional.ofNullable(userService.getByUserId(userId)).orElse(new User());
        orderHelmetLostLog.setAccountId(account.getAccountId());
        orderHelmetLostLog.setAccountName(account.getName());
        orderHelmetLostLog.setUserId(user.getUserId());
        orderHelmetLostLog.setUsername(user.getName());
        orderHelmetLostLog.setPhone(user.getPhone());
        orderHelmetLostLog.setMachineId(machine.getMachineId());
        orderHelmetLostLog.setMachineNo(machine.getMachineNO());
        orderHelmetLostLog.setOrderNo(orderNO);
        orderHelmetLostLog.setHelmetStatus(DisPatchConstant.DISPATCH_HELMET);
        orderHelmetLostLog.setCreateTime(new Date());
        orderHelmetLostLog.setUserCode(machine.getUserCode());
        log.info("添加头盔丢失日志：orderHelmetLostLog：{}", JSON.toJSONString(orderHelmetLostLog));
        orderHelmetLostLogService.create(orderHelmetLostLog);
    }

    @Override
    public Dispatch intelHelmetCheck(String machineNO, Integer terStatus) {
        Dispatch dispatch = null;
        TerInfo terInfo = terInfoDao.getByMachineNO(machineNO);
        if (terInfo == null) {
            log.info("不存在终端版本信息" + machineNO);
            dispatch = new Dispatch(DisPatchConstant.DISPATCH_HELMET, null);
        }
        /**查询软件版本号支持的功能*/
        String version = terInfo.getSoftVersion();
        if (!version.contains("H-") || !version.contains("S-")) {
            log.info(machineNO + "终端版本不支持" + version);
            dispatch = new Dispatch(DisPatchConstant.DISPATCH_HELMET, null);
        } else {
            String soft = StringUtil.getHardCode(version, "S-");
            if (StringUtil.getIndex(soft, 0) && !ObjectUtil.statusParse(0, terStatus)) {
                dispatch = new Dispatch(DisPatchConstant.DISPATCH_HELMET, null);
            } else if (StringUtil.getIndex(soft, 1) && !ObjectUtil.statusParse(1, terStatus)) {
                dispatch = new Dispatch(DisPatchConstant.DISPATCH_HELMET, null);
            } else if (StringUtil.getIndex(soft, 2) && ObjectUtil.statusParse(2, terStatus)) {
                dispatch = new Dispatch(DisPatchConstant.DISPATCH_HELMET, null);
            } else if (StringUtil.getIndex(soft, 3) && !ObjectUtil.statusParse(3, terStatus)) {
                dispatch = new Dispatch(DisPatchConstant.DISPATCH_HELMET, null);
            }
        }
        return dispatch;
    }

    @Override
    public void handlePay(BorrowReturnTer info) {
        if (info.getType() == 0) {
            /***/
            if (!redisService.setNx(RedisConstant.ORDER_NX + info.getOrderNO(), "1", 5)) {
                log.warn("获取分布式锁失败" + info.getOrderNO());
                return;
            }
            try {
                cash(info);
                /**可以还车，清除还车角度校验失败缓存数据*/
                redisService.del(RedisConstant.ANGLE_FAIL_KEY_PREFIX + info.getMachineId());
            } catch (Exception e) {
                log.error("结束订单失败", e);
            } finally {
                redisService.del(RedisConstant.ORDER_NX + info.getOrderNO());
            }

        } else if (info.getType() == 1) {
            //中途停车
            /**添加中途停车记录*/
            Park park = new Park();
            park.setAccountId(info.getAccountId());
            park.setUserId(info.getUserId());
            park.setMachineId(info.getMachineId());
            park.setStartTime(DateTimeUtil.getNowTime());
            park.setRemark("中途停车");

            parkService.insert(park);
            // 临时停车服务消息推送
            wxPushService.pushNotReturnBikeMsg(info.getOrderNO());

        }
    }


    /**
     * 将需要延时分账的用户信息放入缓存
     *
     * @param allMoney      需要扣费总金额
     * @param userMoney     用户余额
     * @param giftCardMoney 礼品卡余额
     * @param couponMoney   优惠券金额
     * @param adAccountId   区域id
     * @param user          用户信息
     */
    public void delay(Double allMoney, Double userMoney, Integer giftCardMoney, Integer couponMoney,
                      Integer adAccountId, User user, String orderNO, AccountConfig accountConfig, String serNO) {
        Double delayMoney = get(user.getAccountId(), AdaPayConstant.ADAPAY_OPEN, allMoney, userMoney, giftCardMoney, couponMoney);
        if (delayMoney == null || delayMoney == 0) {
            return;
        }
        UserDelayPay userDelayPay = new UserDelayPay(user.getUserId(), adAccountId, delayMoney, orderNO, allMoney.intValue());
        if ((accountConfig != null && accountConfig.getAutoLock() == 1) || serNO == null) {


            /**推送至mq*/
            mqProducer.sendToRide(GatewayProtocol.EXCHANGE_PAY_PUSH, RabbitMQData.cashkey, JSON.toJSONString(userDelayPay));
        } else {
            redisService.add(RedisConstant.USER_CASH_FEE + serNO, JSON.toJSONString(userDelayPay), 600);
        }
        log.info("添加延时分账信息" + userDelayPay);

    }

    /**
     * 获取实际支付的金额
     *
     * @param accountId     品牌id
     * @param type          支付类型
     * @param allMoney      总金额
     * @param userMoney     用户余额
     * @param giftCardMoney 礼品卡月
     * @param couponMoney   优惠券金额
     * @return
     */
    public Double get(Integer accountId, Integer type, Double allMoney, Double userMoney, Integer giftCardMoney, Integer couponMoney) {
        BrandRemoteConfig brandConfig = cacheService.getBrandRemoteConfig(accountId, BrandConstant.BRAND_CASH_TYPE);
        Integer payType = 0;
        if (brandConfig != null) {
            payType = Integer.valueOf(brandConfig.getParamKV().substring(brandConfig.getParamKV().indexOf("=") + 1));
        }
        /**实际扣除用户余额,需要分账的金额放入缓存*/
        Double delayMoney;
        /**优先扣除本金*/
        if (payType == 1) {
            if (allMoney >= userMoney) {
                delayMoney = userMoney;
            } else {
                delayMoney = allMoney;
            }
        } else {
            delayMoney = allMoney - giftCardMoney - couponMoney;
            if (delayMoney < 0) {
                delayMoney = 0D;
            }
        }
        return delayMoney;
    }

    /**
     * @description: 根据平台id判断是否开启了意见反馈的借车限制开关
     * @author: mo.shanyong
     * @date: 2024/11/20 10:25
     * @param: accountId 所在的平台id
     * @return: boolean
     */
    public boolean userFeedbackLimit(Integer accountId) {
        AccountConfigExt configExt = accountConfigExtDao.selectByAccountIdAndKey(accountId, AccountConfigExtConstant.USER_FEEDBACK_LIMIT);
        return Objects.nonNull(configExt) && Integer.valueOf(configExt.getParamValue()) == 1;
    }

    /**
     * @description: 检查车辆是否设置了其它头盔锁(除蓝牙头盔锁外)功能
     * @author: mo.shanyong
     * @date: 2025/3/26 11:51
     * @param: machineId 车辆id
     * @param: functionTypeList 要查询的车辆配置的功能类型
     * @return: boolean
     */
    public boolean checkHaveOtherHelmetFuntion(Integer machineId, List<Integer> functionTypeList) {
        Map<String, Object> param = new HashMap<>();
        param.put("machineId", machineId);
       /* List<Integer> list = new ArrayList<>();
        list.add(MachineStateConstant.MACHINE_TYPE_HELMAT);
        list.add(MachineStateConstant.MACHINE_TYPE_INTEL_HELMET);*/
        param.put("list", functionTypeList);
        List<MachineFunction> functions = machineFunctionDao.getHelmet(param);
        return CollectionUtils.isNotEmpty(functions);
    }

    /**
     * @description: 检查该区域的头盔丢失校验开关是否打开了
     * @author: mo.shanyong
     * @date: 2025/3/31 19:09
     * @param: accountId 区域id
     * @return: boolean
     */
    private boolean helmetLostCheckSwitch(Integer accountId) {
        AccountConfigExt accountConfigExtByKey = cacheService.getAccountConfigExtByKey(accountId, AccountConfigExtConstant.helmetLostCheck);
        return Objects.nonNull(accountConfigExtByKey) && Objects.equals(accountConfigExtByKey.getParamValue(), AccountConfigExtConstant.CONFIG_SWITCH_OPEN);
    }

    /**
     * @description: 更新或记录当前用户的体重信息
     * @author: mo.shanyong
     * @date: 2025/4/11 11:00
     * @param: info 骑行订单信息
     * @param: user 登录用户
     * @param: validRide 是否免费骑行
     */
    private void doLogWeight(BorrowReturnTer info, User user, boolean validRide) {
        if (validRide) {
            UserDetailUpdateDTO userDetailUpdate = new UserDetailUpdateDTO();
            userDetailUpdate.setAccountId(info.getAccountId());
            userDetailUpdate.setBrandId(user.getAccountId());
            userDetailUpdate.setOrderNo(info.getOrderNO());
            userDetailUpdate.setUserId(info.getUserId());
            userDetailUpdate.setMachineNo(info.getMachineNO());
            userDetailService.updateWeight(userDetailUpdate);
        } else {
            UserWeightLog userWeightLog = new UserWeightLog();
            userWeightLog.setOrderNo(info.getOrderNO());
            userWeightLog.setStatus(0);
            userWeightLog.setRemark("免费骑行");
            userWeightLogService.updateByOrderNo(userWeightLog);
        }
    }

    /**
     * @description: 判断骑行订单是否有效
     * @author: mo.shanyong
     * @date: 2025/4/11 11:04
     * @param: rideLogDetails
     * @param: rideLog
     * @param: money
     * @return: boolean
     */

    private boolean validRide(RideLogDetails rideLogDetails, RideLog rideLog, Double money) {
        /*
            满足任一条件，订单判断有效，否则无效
                1、金额：订单金额/礼品卡抵扣/卡券抵扣不为0
                2、时长：骑行时长大于等于3分钟
        */
        Long cardDeductionMoney = Optional.ofNullable(rideLogDetails.getCardDeductionMoney()).orElse(0L);
        Long giftCardDeductionMoney = Optional.ofNullable(rideLogDetails.getGiftCardDeductionMoney()).orElse(0L);
        LocalDateTime startDateTime = DateUtil.parseLocalDateTime(rideLog.getEndTime());
        LocalDateTime endDateTime = DateUtil.parseLocalDateTime(rideLog.getStartTime());
        long rideTime = Duration.between(startDateTime, endDateTime).getSeconds();
        return money != 0 || cardDeductionMoney != 0 || giftCardDeductionMoney != 0 || rideTime >= 180;
    }

    /**
     * @description: 检查当前订单是否符合标记或消除车辆疑似故障的标志
     * @author: mo.shanyong
     * @date: 2025/5/13 14:23
     * @param: rideLog 骑行订单
     */
    private void checkSuspectedFault(RideLog rideLog, double lo, double la) {
        log.info("检查疑似故障------rideLog:{}", JSON.toJSONString(rideLog));
        int betweenSecond = (int) ChronoUnit.SECONDS.between(DateTimeUtil.getLocalDateTimeByStr(rideLog.getStartTime()), DateTimeUtil.getLocalDateTimeByStr(rideLog.getEndTime()));
        Integer accountId = rideLog.getAccountId();
        List<AccountConfigExt> accountConfigExtList = cacheService.getAccountConfigExtByKeys(accountId,
                new ArrayList<>(Arrays.asList(AccountConfigExtConstant.SUSPECTED_FAULT_SWITCH, AccountConfigExtConstant.SUSPECTED_FAULT_ORDER_NUM, AccountConfigExtConstant.SUSPECTED_FAULT_RIDE_TIME,
                        AccountConfigExtConstant.SUSPECTED_FAULT_RIDE_DISTANCE, AccountConfigExtConstant.SUSPECTED_FAULT_CANCEL_RIDE_TIME, AccountConfigExtConstant.SUSPECTED_FAULT_CANCEL_RIDE_DISTANCE)));
        if (CollectionUtils.isEmpty(accountConfigExtList)) {
            return;
        }
        Map<String, String> configExtMap = accountConfigExtList.stream().collect(Collectors.toMap(AccountConfigExt::getParamKey, AccountConfigExt::getParamValue));
        //设置的连续订单数
        int suspectedFaultOrderNum = Integer.parseInt(configExtMap.getOrDefault(AccountConfigExtConstant.SUSPECTED_FAULT_ORDER_NUM, "0"));
        //小于的时间，分钟
        int suspectedFaultRideTime = Integer.parseInt(configExtMap.getOrDefault(AccountConfigExtConstant.SUSPECTED_FAULT_RIDE_TIME, "0"));
        //骑行小于的距离
        int suspectedFaultRideDistance = Integer.parseInt(configExtMap.getOrDefault(AccountConfigExtConstant.SUSPECTED_FAULT_RIDE_DISTANCE, "0"));

        if (!Objects.equals(configExtMap.get(AccountConfigExtConstant.SUSPECTED_FAULT_SWITCH), AccountConfigExtConstant.CONFIG_SWITCH_OPEN)) {
            return;
        }
        MachineFault machineFault = machineFaultService.getByMachineId(rideLog.getMachineId());
        if (Objects.nonNull(machineFault)) {
            machineFaultThreadLocal.set(machineFault);
        }
        log.info("检查疑似故障--machineFault:{},configExtMap:{}", JSON.toJSONString(machineFault), JSON.toJSONString(configExtMap));
        //检查是否添加疑似故障
        if (suspectedFaultOrderNum > 0 && (suspectedFaultRideTime > 0 || suspectedFaultRideDistance > 0) && Objects.isNull(machineFault)) {
            this.addSuspectedFault(rideLog, betweenSecond, suspectedFaultOrderNum, suspectedFaultRideTime, suspectedFaultRideDistance, lo, la);
        }
        //检查是否消除疑似故障
        this.cancelSuspectedFault(rideLog, betweenSecond, configExtMap);
        machineFaultThreadLocal.remove();
    }

    /**
     * @description: 检查是否符合添加疑似故障标志，并添加
     * @author: mo.shanyong
     * @date: 2025/5/13 14:24
     * @param: rideLog
     * @param: betweenSecond 订单骑行的秒数
     * @param: suspectedFaultOrderNum 设置的连续订单数
     * @param: suspectedFaultRideTime 小于的时间，分钟
     * @param: suspectedFaultRideDistance 骑行小于的距离
     */
    private void addSuspectedFault(RideLog rideLog, int betweenSecond, int suspectedFaultOrderNum,
                                   int suspectedFaultRideTime, int suspectedFaultRideDistance, double lo, double la) {
        int mileage = rideLog.getMileage().intValue();
        //先判断当前订单是否符合设置的条件
        if ((suspectedFaultRideTime != 0 && betweenSecond > (suspectedFaultRideTime * 60)) || (suspectedFaultRideDistance != 0 && (mileage * 1000) > suspectedFaultRideDistance)) {
            return;
        }
        List<RideLogDetails> rideLogDetailsList = rideLogDetailsService.getLastLimitByMachineId(rideLog.getMachineId(), suspectedFaultOrderNum);
        //如果返回的订单量不够则不记
        if (Objects.isNull(rideLogDetailsList) || rideLogDetailsList.size() < suspectedFaultOrderNum) {
            return;
        }
        for (RideLogDetails rideLogDetails : rideLogDetailsList) {
            if (Objects.equals(rideLogDetails.getOrderId(), rideLog.getOrderNO())) {
                continue;
            }
            int rideSecond = (int) ChronoUnit.SECONDS.between(DateTimeUtil.getLocalDateTimeByDate(rideLogDetails.getOrderStartTime()),
                    DateTimeUtil.getLocalDateTimeByDate(rideLogDetails.getOrderEndTime()));
            Integer rideOrderMileage = rideLogDetails.getMileage();
            //只要有一个订单不符合条件，则不记录疑似故障
            if ((suspectedFaultRideTime != 0 && rideSecond > (suspectedFaultRideTime * 60)) || (suspectedFaultRideDistance != 0 && rideOrderMileage > suspectedFaultRideDistance)) {
                return;
            }
        }
        //记录疑似故障
        this.addMachineSuspectedFault(rideLog, lo, la);
    }

    /**
     * @description: 记录车辆疑似故障
     * @author: mo.shanyong
     * @date: 2025/5/13 15:35
     * @param: rideLog 骑行订单
     * @param: lo 经度
     * @param: la 维度
     */
    private void addMachineSuspectedFault(RideLog rideLog, double lo, double la) {
        MachineFault machineFault = new MachineFault();
        machineFault.setFaults("疑似故障");
        machineFault.setMachineNO(rideLog.getMachineNO());
        machineFault.setMachineId(rideLog.getMachineId());
        machineFault.setRemark("疑似故障");
        machineFault.setFrom(4);
        machineFault.setOrderNO(rideLog.getOrderNO());
        machineFault.setAddMan("系统");
        machineFault.setUpdateTime(rideLog.getEndTime());
        machineFault.setPoint(lo + "," + la);
        machineFaultService.add(machineFault);
    }

    /**
     * @description: 取消疑似故障
     * @author: mo.shanyong
     * @date: 2025/5/13 15:49
     * @param: rideLog
     * @param: betweenSecond
     * @param: configExtMap
     */

    private void cancelSuspectedFault(RideLog rideLog, int betweenSecond, Map<String, String> configExtMap) {
        MachineFault machineFault = machineFaultThreadLocal.get();
        //如果不存在故障，或者存在的故障不是疑似故障，则不需要消除疑似故障
        if (Objects.isNull(machineFault) || !Objects.equals(machineFault.getFrom(), 4)) {
            return;
        }
        //骑行大于的时间，分钟
        int suspectedFaultCancelRideTime = Integer.parseInt(configExtMap.getOrDefault(AccountConfigExtConstant.SUSPECTED_FAULT_CANCEL_RIDE_TIME, "0"));
        //骑行大于的距离
        int suspectedFaultCancelRideDistance = Integer.parseInt(configExtMap.getOrDefault(AccountConfigExtConstant.SUSPECTED_FAULT_CANCEL_RIDE_DISTANCE, "0"));
        if (suspectedFaultCancelRideTime <= 0 && suspectedFaultCancelRideDistance <= 0) {
            return;
        }
        double mileage = Objects.nonNull(rideLog.getMileage()) ? rideLog.getMileage() : 0;
        if ((suspectedFaultCancelRideTime != 0 && betweenSecond >= (suspectedFaultCancelRideTime * 60))
                || (suspectedFaultCancelRideDistance != 0 && (mileage * 1000) >= suspectedFaultCancelRideDistance)) {
            //取消疑似故障
            machineFaultService.del(rideLog.getMachineId());
        }
    }

}
